PageSourceSearch

https://www.bookyawards.com/book-award-submission

html bookyawards.com collected 2026-10-09 03:14:29 UTC 78,455 bytes, 1,807 lines download raw bytes

1<!DOCTYPE html>
2<html lang="en">
3<head>
4  
5  
6  
7  
8  
9  
10  <meta charset="UTF-8">
11  <meta name="viewport" content="width=device-width, initial-scale=1.0">
12  <meta name="description" content="Book your award submission for published books. Free screening or paid Booky Award judging — learn submission options, tiers, and pricing.">
13  <link rel="canonical" href="https://www.bookyawards.com/book-award-submission">
14  <title>Book Your Award Submission — BookyAwards — BookyAwards</title>
15  <meta property="og:title" content="Book Your Award Submission — BookyAwards">
16  <meta property="og:description" content="Book your award submission for published books. Free screening or paid Booky Award judging — learn submission options, tiers, and pricing.">
17  <meta property="og:url" content="https://www.bookyawards.com/book-award-submission">
18  <meta property="og:image" content="https://www.bookyawards.com/static/img/booky_trophy.png">
19  <meta property="og:type" content="website">
20  <meta name="twitter:card" content="summary_large_image">
21  <meta name="twitter:title" content="Book Your Award Submission — BookyAwards">
22  <meta name="twitter:description" content="Book your award submission for published books. Free screening or paid Booky Award judging — learn submission options, tiers, and pricing.">
23  <meta name="twitter:image" content="https://www.bookyawards.com/static/img/booky_trophy.png">
24
25  <link rel="preconnect" href="https://fonts.googleapis.com">
26  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
27  <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=Playfair+Display:wght@400;600;700;900&display=swap" rel="stylesheet">
28  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css">
29  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/font/bootstrap-icons.min.css">
30  <link rel="stylesheet" href="/static/css/style.css?v=20260905">
31  <link rel="icon" type="image/png" href="/static/img/favicon.png">
32  <style>
33    :root {
34      --ba-burgundy:      #6B1019;
35      --ba-burgundy-deep: #3A0A11;
36      --ba-gold:          #D4AF37;
37      --ba-gold-deep:     #A8862A;
38      --ba-cream:         #FAF6F1;
39      --ba-paper:         #FFFFFF;
40      --ba-text:          #2A1A1C;
41      --ba-muted:         #6B5B5C;
42      --ba-border:        rgba(42, 26, 28, 0.10);
43
44      --primary-color:   var(--ba-burgundy);
45      --secondary-color: var(--ba-gold);
46      --page-bg:         #FFFFFF;
47      --surface-bg:      var(--ba-paper);
48      --surface-alt-bg:  var(--ba-cream);
49      --border-color:    var(--ba-border);
50      --text-dark:       var(--ba-text);
51      --muted-text:      var(--ba-muted);
52      --font-body: 'Inter', system-ui, sans-serif;
53      --font-display: 'Playfair Display', 'Georgia', serif;
54
55      /* Burgundy navbar with light gold logo + light text. Footer darker burgundy. */
56      --mm-brand-text:   #FFFFFF;
57      --mm-accent-text:  var(--ba-text);
58      --mm-navbar-bg:    var(--ba-burgundy);
59      --mm-navbar-text:  #FFFFFF;
60      --mm-footer-bg:    var(--ba-burgundy-deep);
61      --mm-footer-text:  #E8D6CE;
62      --mm-accent:       var(--ba-gold);
63    }
64    *, *::before, *::after { box-sizing: border-box; }
65    html, body { max-width: 100%; overflow-x: hidden; }
66    img, svg, video, iframe { max-width: 100%; height: auto; }
67    body {
68      background: #FFFFFF;
69      color: var(--ba-text);
70      font-family: var(--font-body);
71    }
72    h1, h2, h3, h4 {
73      color: var(--ba-burgundy);
74      font-family: var(--font-display);
75      letter-spacing: 0;
76      font-weight: 600;
77    }
78    a { color: var(--ba-burgundy); text-decoration-color: var(--ba-gold); text-underline-offset: 3px; }
79    a:hover { color: var(--ba-gold-deep); }
80
81    .navbar { border-bottom: 1px solid rgba(255,255,255,0.08); }
82
83    /* Left-justify the icon + label inside dropdown menu items, while keeping
84       the 44px min-height tap target (the navbar accessibility rule below
85       centres other nav links but no longer touches .dropdown-item). */
86    .hcwf-navbar .dropdown-item {
87      display: flex;
88      align-items: center;
89      justify-content: flex-start;
90      text-align: left;
91      min-height: 44px;
92    }
93
94    .btn-primary-cta {
95      background: var(--ba-gold);
96      border: 1px solid var(--ba-gold);
97      color: var(--ba-text);
98      font-weight: 700;
99      padding: .75rem 1.6rem;
100      border-radius: 999px;
101      box-shadow: 0 2px 12px rgba(212, 175, 55, 0.32);
102    }
103    .btn-primary-cta:hover { background: var(--ba-gold-deep); border-color: var(--ba-gold-deep); color: #FFFFFF; }
104
105    .btn-ghost {
106      background: transparent;
107      border: 1px solid var(--ba-burgundy);
108      color: var(--ba-burgundy);
109      font-weight: 600;
110      padding: .7rem 1.4rem;
111      border-radius: 999px;
112    }
113    .btn-ghost:hover { background: var(--ba-burgundy); color: #FFFFFF; }
114
115    .btn-ghost-light {
116      background: transparent;
117      border: 1px solid #FFFFFF;
118      color: #FFFFFF;
119      font-weight: 600;
120      padding: .7rem 1.4rem;
121      border-radius: 999px;
122    }
123    .btn-ghost-light:hover { background: #FFFFFF; color: var(--ba-burgundy); }
124
125    .site-footer { background: var(--mm-footer-bg); color: var(--mm-footer-text); padding: 2rem 0; }
126    .site-footer a { color: var(--mm-footer-text); }
127    .site-footer a:hover { color: var(--ba-gold); }
128
129    .accent-rule {
130      border: 0;
131      height: 2px;
132      background: linear-gradient(90deg, transparent, var(--ba-gold), transparent);
133      margin: 1rem auto;
134      max-width: 8rem;
135    }
136
137    .badge-gold {
138      background: var(--ba-gold);
139      color: var(--ba-text);
140      font-weight: 700;
141      letter-spacing: 0.04em;
142      padding: .25rem .8rem;
143      border-radius: 999px;
144      font-size: .875rem;
145    }
146    .hcwf-navbar .hcwf-logo-link,
147    .hcwf-navbar .hcwf-support-phone,
148    .hcwf-navbar .nav-link,
149    .site-footer a,
150    footer a {
151      min-height: 44px;
152      min-width: 44px;
153      display: inline-flex;
154      align-items: center;
155      justify-content: center;
156    }
157    .hcwf-navbar .hcwf-support-phone { white-space: normal; }
158  </style>
159
160  
160<script type="application/ld+json">
161  {
162    "@context": "https://schema.org",
163    "@graph": [
164      {
165        "@type": "Organization",
166        "@id": "https://www.bookyawards.com/#organization",
167        "name": "BookyAwards",
168        "url": "https://www.bookyawards.com",
169        "logo": "https://www.bookyawards.com/static/img/logo.png",
170        "description": "AI-judged book awards. Every entry receives an honest, category-specific Booky based on what&#39;s genuinely strong about the book — or your money back if our judges can’t award one in good conscience.",
171        "parentOrganization": {
172          "@type": "Organization",
173          "name": "Archieboy Holdings, LLC",
174          "url": "https://www.archieboy.com"
175        }
176      },
177      {
178        "@type": "WebSite",
179        "@id": "https://www.bookyawards.com/#website",
180        "url": "https://www.bookyawards.com",
181        "name": "BookyAwards",
182        "description": "AI-judged book awards. Every entry receives an honest, category-specific Booky based on what&#39;s genuinely strong about the book — or your money back if our judges can’t award one in good conscience.",
183        "publisher": {"@id": "https://www.bookyawards.com/#organization"}
184      },
185      {
186        "@type": "Service",
187        "@id": "https://www.bookyawards.com/#service",
188        "name": "BookyAwards book award judging",
189        "serviceType": "AI-judged book awards for authors and publishers",
190        "provider": {"@id": "https://www.bookyawards.com/#organization"},
191        "areaServed": "Worldwide",
192        "description": "AI-judged book awards. Every entry receives an honest, category-specific Booky based on what&#39;s genuinely strong about the book — or your money back if our judges can’t award one in good conscience.",
193        "offers": {
194          "@type": "AggregateOffer",
195          "priceCurrency": "USD",
196          "lowPrice": 0,
197          "highPrice": 129,
198          "offerCount": 3,
199          "offers": [
200            {
201              "@type": "Offer",
202              "name": "Free Screen",
203              "price": 0,
204              "priceCurrency": "USD",
205              "description": "A free AI screen of your manuscript that tells you whether your book would qualify for a Booky Award, before you pay anything.",
206              "url": "https://www.bookyawards.com/screen"
207            },
208            {
209              "@type": "Offer",
210              "name": "Booky Award",
211              "price": 39,
212              "priceCurrency": "USD",
213              "description": "Full 10-axis judging by your category’s specialist judge. Permanent award page, PDF certificate, embeddable badge.",
214              "url": "https://www.bookyawards.com/submit"
215            },
216            {
217              "@type": "Offer",
218              "name": "Author Bundle",
219              "price": 129,
220              "priceCurrency": "USD",
221              "description": "Everything in Booky Award + a custom press release written for your book (yours to send wherever you like), 8-graphic social bundle, 30-second AI-narrated audio reel, cover variant with your award badge, entry into the annual Book of the Year shortlist.",
222              "url": "https://www.bookyawards.com/submit"
223            }
224          ]
225        }
226      }
227    ]
228  }
229  </script>
vendor: 76 bytes, lines 229-234
229
230  
231
232  
233  
234  <script async src="https://www.googletagmanager.com/gtag/js?id=
234AW-11496617174
vendor: 14 bytes, lines 234-235
234"></script>
235  
235<script>
vendor: 121 bytes, lines 235-236
235window.dataLayer=window.dataLayer||[];function gtag(){dataLayer.push(arguments);}gtag('js',new Date());
236  gtag('config','
236AW-11496617174
vendor: 9 bytes, lines 236-238
236');
237  
238  
238</script>
238
239  
240  
241  
241<script>
vendor: 388 bytes, line 241
241!function(f,b,e,v,n,t,s){if(f.fbq)return;n=f.fbq=function(){n.callMethod?n.callMethod.apply(n,arguments):n.queue.push(arguments)};if(!f._fbq)f._fbq=n;n.push=n;n.loaded=!0;n.version='2.0';n.queue=[];t=b.createElement(e);t.async=!0;t.src=v;s=b.getElementsByTagName(e)[0];s.parentNode.insertBefore(t,s)}(window,document,'script','https://connect.facebook.net/en_US/fbevents.js');fbq('init','
241320704642247362
vendor: 27 bytes, line 241
241');fbq('track','PageView');
241</script>
241
242  
243
244  
245  
246  
247</head>
248<body>
249  
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290<nav class="navbar navbar-expand-xl sticky-top shadow-sm hcwf-navbar" style="background: var(--mm-navbar-bg, var(--primary-color, #2c3e50)); --hcwf-navbar-text: #FFFFFF; padding: 8px 0; z-index: 1030;">
291    
292    <style>
293      .hcwf-navbar .container-fluid {
294        display: flex;
295        align-items: center;
296        justify-content: space-between;
297      }
298      .hcwf-navbar .navbar-brand {
299        flex: 0 0 min(350px, calc(100% - 84px));
300        min-width: 0;
301        width: min(350px, calc(100% - 84px));
302        max-width: min(350px, calc(100% - 84px));
303        margin-right: .75rem;
304        overflow: visible;
305        padding: 0;
306      }
307      .hcwf-navbar .hcwf-brand-stack {
308        display: flex;
309        flex-direction: column;
310        align-items: flex-start;
311        gap: 2px;
312      }
313      .hcwf-navbar .hcwf-logo-link {
314        display: block;
315        width: 350px;
316        max-width: 100%;
317        line-height: 0;
318      }
319      .hcwf-navbar .navbar-brand img {
320        width: 350px !important;
321        max-width: 100% !important;
322        height: auto;
323        max-height: none;
324        object-fit: contain;
325        object-position: left center;
326        display: block;
327      }
328      
329      .hcwf-navbar .hcwf-support-phone {
330        align-items: center;
331        color: var(--hcwf-navbar-text, #FFFFFF) !important;
332        display: flex;
333        font-size: .875rem;
334        font-weight: 600;
335        justify-content: center;
336        line-height: 1.2;
337        min-height: 44px;
338        opacity: .9;
339        padding: .25rem .5rem;
340        text-decoration: none;
341        text-align: center;
342        white-space: nowrap;
343        width: 100%;
344      }
345      .hcwf-navbar .hcwf-support-phone:hover {
346        opacity: 1;
347        text-decoration: underline;
348      }
349      .hcwf-navbar .navbar-toggler {
350        flex: 0 0 58px;
351        width: 58px;
352        height: 58px;
353        margin-left: auto;
354        display: inline-flex;
355        align-items: center;
356        justify-content: center;
357        padding: 0;
358        border-color: rgba(255,255,255,.35);
359        color: var(--hcwf-navbar-text, #FFFFFF) !important;
360      }
361      .hcwf-navbar .navbar-toggler i,
362      .hcwf-navbar .navbar-toggler .bi,
363      .hcwf-navbar .nav-link {
364        color: var(--hcwf-navbar-text, #FFFFFF) !important;
365      }
366      
367      
368      .hcwf-navbar .nav-link.hcwf-nav-cta,
369      nav.hcwf-navbar .navbar-nav > .nav-item > .nav-link.hcwf-nav-cta {
370        color: var(--mm-accent-text, var(--hcwf-navbar-text, #FFFFFF)) !important;
371      }
372      @media (min-width: 75rem) {
373        .hcwf-navbar .navbar-toggler {
374          display: none !important;
375        }
376      }
377      @media (max-width: 74.9375rem) {
378        .hcwf-navbar .container-fluid {
379          flex-wrap: nowrap !important;
380          gap: .5rem;
381        }
382        .hcwf-navbar .navbar-brand {
383          flex-basis: calc(100% - 74px);
384          width: calc(100% - 74px);
385          max-width: calc(100% - 74px);
386        }
387        .hcwf-navbar .navbar-brand img {
388          width: 100%;
389          max-width: 350px !important;
390        }
391        .hcwf-navbar .navbar-collapse {
392          position: absolute;
393          top: 100%;
394          left: 0;
395          right: 0;
396          background: var(--mm-navbar-bg, var(--primary-color, #2c3e50));
397          padding: .75rem 1rem 1rem;
398          border-top: 1px solid rgba(255,255,255,.08);
399          box-shadow: 0 12px 24px rgba(0,0,0,.22);
400        }
401        .hcwf-navbar .navbar-nav {
402          align-items: flex-start !important;
403        }
404      }
405      @media (max-width: 767px) {
406        .hcwf-navbar {
407          padding-top: 6px !important;
408          padding-bottom: 6px !important;
409        }
410        .hcwf-navbar .container-fluid {
411          gap: .35rem;
412        }
413        .hcwf-navbar .navbar-brand {
414          flex-basis: calc(100% - 66px);
415          width: calc(100% - 66px);
416          max-width: calc(100% - 66px);
417          margin-right: .35rem;
418        }
419        .hcwf-navbar .navbar-brand img {
420          max-width: min(350px, 100%) !important;
421        }
422        .hcwf-navbar .hcwf-support-phone {
423          font-size: .875rem;
424        }
425        .hcwf-navbar .navbar-toggler {
426          flex-basis: 52px;
427          width: 52px;
428          height: 52px;
429        }
430      }
431    </style>
432    
433    <div class="container-fluid d-flex align-items-center justify-content-between">
434        <div class="navbar-brand hcwf-brand-stack">
435            <a class="hcwf-logo-link" href="/" aria-label="BookyA
435wards home">
436                <img src="https://www.bookyawards.com/static/img/logo.png"
437                     srcset="https://www.bookyawards.com/static/img/logo-320.png 320w, https://www.bookyawards.com/static/img/logo-640.png 640w, https://www.bookyawards.com/static/img/logo.png 1022w"
438                     sizes="(max-width: 767px) 70vw, 350px"
439                     alt="BookyAwards"
440                     decoding="async">
441            </a>
442            
443            <a class="hcwf-support-phone" href="tel:+19786438662">24/7 Sales / Support: +1 (978) 643-8662</a>
444            
445        </div>
446        <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-label="Toggle navigation">
447            <i class="bi bi-list"></i>
448        </button>
449        <div class="collapse navbar-collapse" id="navbarNav">
450            <ul class="navbar-nav ms-auto align-items-center">
451                
452                <li class="nav-item"><a class="nav-link px-3" href="/" style="color: var(--hcwf-navbar-text) !important;">Home</a></li>
453                
454                
455                <li class="nav-item"><a class="nav-link px-3" href="/#pricing" style="color: var(--hcwf-navbar-text) !important;">Pricing</a></li>
456                
457                <li class="nav-item dropdown">
458                    <a class="nav-link px-3 dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false" style="color: var(--hcwf-navbar-text) !important;">Resources</a>
459                    <ul class="dropdown-menu dropdown-menu-end shadow border-0">
460                        <li><a class="dropdown-item py-2" href="/blog"><i class="bi bi-journal-text me-2"></i>Blog</a></li>
461                        
462                        
463                        <li><a class="dropdown-item py-2" href="/indie-book-awards"><i class="bi bi-award me-2"></i>Indie Book Awards</a></li>
464                        
465                        <li><a class="dropdown-item py-2" href="/book-award-submission"><i class="bi bi-upload me-2"></i>Book Award Submission</a></li>
466                        
467                        <li><a class="dropdown-item py-2" href="/fiction-book-awards"><i class="bi bi-book me-2"></i>Fiction Book Awards</a></li>
468                        
469                        <li><a class="dropdown-item py-2" href="/romance-book-awards"><i class="bi bi-heart me-2"></i>Romance Book Awards</a></li>
470                        
471                        <li><a class="dropdown-item py-2" href="/nonfiction-book-awards"><i class="bi bi-journal-richtext me-2"></i>Nonfiction Book Awards</a></li>
472                        
473                        <li><a class="dropdown-item py-2" href="/book-award-badge"><i class="bi bi-patch-check me-2"></i>Book Award Badge</a></li>
474                        
475                        <li><a class="dropdown-item py-2" href="/book-awards-list"><i class="bi bi-list-stars me-2"></i>Book Awards Guide</a></li>
476                        
477                        <li><a class="dropdown-item py-2" href="/bookyawards-vs-readers-favorite"><i class="bi bi-columns-gap me-2"></i>Booky vs Readers’ Favorite</a></li>
478                        
479                        <li><a class="dropdown-item py-2" href="/bookyawards-vs-ippy-awards"><i class="bi bi-columns-gap me-2"></i>Booky vs IPPY Awards</a></li>
480                        
481                        <li><a class="dropdown-item py-2" href="/ai-book-awards"><i class="bi bi-cpu me-2"></i>AI Book Awards</a></li>
482                        
483                        <li><a class="dropdown-item py-2" href="/how-it-works"><i class="bi bi-list-check me-2"></i>How It Works</a></li>
484                        
485                        <li><a class="dropdown-item py-2" href="/judges"><i class="bi bi-people me-2"></i>Judges</a></li>
486                        
487                        <li><a class="dropdown-item py-2" href="/rubric"><i class="bi bi-card-checklist me-2"></i>Rubric</a></li>
488                        
489                        <li><a class="dropdown-item py-2" href="/winners"><i class="bi bi-trophy me-2"></i>Winners</a></li>
490                        
491                        <li><a class="dropdown-item py-2" href="/screen"><i class="bi bi-shield-check me-2"></i>Free Book Award Screen</a></li>
492                        
493                        <li><a class="dropdown-item py-2" href="https://www.archieboy.com/affiliate/"><i class="bi bi-cash-coin me-2"></i>Affiliate Program</a></li>
494                        
495                        <li><a class="dropdown-item py-2" href="/faq"><i class="bi bi-question-circle me-2"></i>FAQ</a></li>
496                    </ul>
497                </li>
498                
499                
500                
501                
502                <li class="nav-item"><a class="nav-link px-3" href="/how-it-works" style="color: var(--hcwf-navbar-text) !important;">How It Works</a></li>
503                
504                
505                
506                <li class="nav-item"><a class="nav-link px-3" href="https://www.archieboy.com/affiliate/" style="color: var(--hcwf-navbar-text) !important;">Affiliates</a></li>
507                
508                
509                
510                
511                <li class="nav-item"><a class="nav-link px-3" href="/contact" style="color: var(--hcwf-navbar-text) !important;">Contact</a></li>
512
513                
514                
515
516
517
518
519
520
521
522<li class="nav-item"><a class="nav-link px-3 text-white" href="/auth.html" style="font-weight: 600;">Login</a></li>
523<li class="nav-item"><a class="nav-link px-3 ms-lg-2 hcwf-nav-cta" href="/register" style="background: var(--mm-accent, #228B22); border-radius: 50px; padding: 8px 25px; font-weight: 600;">Join Free</a></li>
524
525            </ul>
526        </div>
527    </div>
528</nav>
529
530  <main>
531
532<section style="padding: 5rem 1rem 4rem; background: linear-gradient(180deg, #FFFFFF 0%, var(--ba-cream) 100%);">
533  <div class="container text-center" style="max-width: 820px;">
534    <span class="badge-gold mb-4 d-inline-block">SUBMISSION PATH</span>
535    <h1 class="display-4 mt-3">Submit a Published Book for Honest Award Judging</h1>
536    <hr class="accent-rule">
537    <p class="lead mt-3" style="color: var(--ba-muted);">
537BookyAwards is a book award submission path for completed books, not a short-story contest or unpublished manuscript prize.</p>
538    <div class="mt-4 d-flex flex-wrap gap-3 justify-content-center">
539      <a href="/submit" class="btn btn-primary-cta">Submit Your Book</a>
540      <a href="/rubric" class="btn btn-ghost">See the Rubric</a>
541    </div>
542  </div>
543</section>
544
545
546
547
548<section style="padding: 4rem 1rem; background: #FFFFFF;">
549  <div class="container" style="max-width: 1080px;">
550    <div class="row g-4">
551      
552      <div class="col-lg-4">
553        <article style="background: var(--ba-cream); border: 1px solid var(--ba-border); border-radius: 16px; padding: 1.5rem; height: 100%;">
554          <h2 class="h4">Start with a free screen</h2>
555          <p style="color: var(--ba-muted); margin-bottom: 0;">The free screen tells you whether your book appears likely to qualify before you buy an entry. It is not a cash-prize contest; it is a no-card qualification check.</p>
556        </article>
557      </div>
558      
559      <div class="col-lg-4">
560        <article style="background: var(--ba-cream); border: 1px solid var(--ba-border); border-radius: 16px; padding: 1.5rem; height: 100%;">
561          <h2 class="h4">Choose an award tier</h2>
562          <p style="color: var(--ba-muted); margin-bottom: 0;">The Booky Award tier includes full judging, a permanent award page, certificate, and badge. The Author Bundle adds promotional assets authors can distribute themselves.</p>
563        </article>
564      </div>
565      
566      <div class="col-lg-4">
567        <article style="background: var(--ba-cream); border: 1px solid var(--ba-border); border-radius: 16px; padding: 1.5rem; height: 100%;">
568          <h2 class="h4">Why it is not a traditional contest</h2>
569          <p style="color: var(--ba-muted); margin-bottom: 0;">There are no seasonal judging windows, celebrity panels, or vague finalist lists. The value is a transparent award page with category-specific reasoning and rubric scores.</p>
570        </article>
571      </div>
572      
573      <div class="col-lg-4">
574        <article style="background: var(--ba-cream); border: 1px solid var(--ba-border); border-radius: 16px; padding: 1.5rem; height: 100%;">
575          <h2 class="h4">What to prepare before submission</h2>
576          <p style="color: var(--ba-muted); margin-bottom: 0;">Have the published title, author name, genre, ASIN or ISBN, manuscript file, and cover ready before you begin. The submission form asks for enough metadata to route the book to the right judge and to build useful award assets if it wins.</p>
577        </article>
578      </div>
579      
580      <div class="col-lg-4">
581        <article style="background: var(--ba-cream); border: 1px solid var(--ba-border); border-radius: 16px; padding: 1.5rem; height: 100%;">
582          <h2 class="h4">How to strengthen the entry</h2>
583          <p style="color: var(--ba-muted); margin-bottom: 0;">A strong award submission is clear, complete, and honest about genre. If you are unsure what to emphasize, read <a href="/blog/how-to-write-a-strong-book-award-submission">How to Write a Strong Book Award Submission</a> before buying entries.</p>
584        </article>
585      </div>
586      
587      <div class="col-lg-4">
588        <article style="background: var(--ba-cream); border: 1px solid var(--ba-border); border-radius: 16px; padding: 1.5rem; height: 100%;">
589          <h2 class="h4">What happens after judging</h2>
590          <p style="color: var(--ba-muted); margin-bottom: 0;">If the book earns a Booky, the award page explains why. If it does not qualify, the honest-screen model protects authors from paying for empty praise and gives a clearer next step.</p>
591        </article>
592      </div>
593      
594      <div class="col-lg-4">
595        <article style="background: var(--ba-cream); border: 1px solid var(--ba-border); border-radius: 16px; padding: 1.5rem; height: 100%;">
596          <h2 class="h4">Submission versus contest entry</h2>
597          <p style="color: var(--ba-muted); margin-bottom: 0;">A contest often compares many books in one seasonal batch. BookyAwards evaluates a published book against a consistent rubric and creates a public re
597cord when it wins. That makes the submission useful even outside a contest calendar.</p>
598        </article>
599      </div>
600      
601      <div class="col-lg-4">
602        <article style="background: var(--ba-cream); border: 1px solid var(--ba-border); border-radius: 16px; padding: 1.5rem; height: 100%;">
603          <h2 class="h4">Why the free screen matters</h2>
604          <p style="color: var(--ba-muted); margin-bottom: 0;">The free screen is designed to reduce wasted entry fees. Authors can learn whether a book appears award-ready before buying full judging, which keeps the process aligned with BookyAwards’ honest-or-free promise.</p>
605        </article>
606      </div>
607      
608      <div class="col-lg-4">
609        <article style="background: var(--ba-cream); border: 1px solid var(--ba-border); border-radius: 16px; padding: 1.5rem; height: 100%;">
610          <h2 class="h4">Best fit for published books</h2>
611          <p style="color: var(--ba-muted); margin-bottom: 0;">BookyAwards is strongest for authors with a finished, available book who want a verifiable award page, badge, certificate, and optional promotional assets. It is not meant for incomplete manuscripts or private writing feedback.</p>
612        </article>
613      </div>
614      
615    </div>
616  </div>
617</section>
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633<section style="background: var(--ba-burgundy); color: #FFFFFF; padding: 4rem 1rem; text-align: center;">
634  <div class="container">
635    
636    <h2 style="color: #FFFFFF;">Find out what your book does best.</h2>
637    <p class="lead mt-3" style="color: rgba(255,255,255,0.85); max-width: 680px; margin: 0 auto;">
638      Start with the free screen, then submit for full judging when you are ready for a permanent award page and author-ready assets.
639    </p>
640    <a href="/screen" class="btn btn-primary-cta mt-4">Try the Free Screen</a>
641    
642  </div>
643</section>
644</main>
645
646  
647
648
649<footer id="footer" class="site-footer" style="padding: 50px 0 20px 0; font-size: 14px; font-family: var(--font-body);">
650    <div class="container">
651        <div class="row">
652            <div class="col-lg-3 col-md-6 mb-4">
653                <div class="mb-3">
654                    <a href="/" aria-label="BookyAwards home" style="min-height:44px; display:inline-flex; align-items:center;">
655                        <img src="https://www.bookyawards.com/static/img/logo.png"
656                             srcset="https://www.bookyawards.com/static/img/logo-320.png 320w, https://www.bookyawards.com/static/img/logo-640.png 640w, https://www.bookyawards.com/static/img/logo.png 1022w"
657                             sizes="(max-width: 767px) 70vw, 350px"
658                             alt="BookyAwards" loading="lazy" decoding="async"
659                             style="width: 250px; max-width: 100%; height: auto; display: block;">
660                    </a>
661                </div>
662                <p style="line-height: 1.8; margin: 0; color: var(--mm-footer-text);">
663                    Archieboy Holdings, LLC<br>
664                    #1006, 771 Boston Post Rd STE 11<br>
665                    Marlborough, MA 01752<br>
666                    Phone: +1 978-643-8662<br>
667                    Text: +1 978-643-8662<br>
668                    Email: <a href="#" class="footer-email-link" aria-label="Email BookyAwards"
669                        data-name="hello" data-domain="bookyawards.com"
670                        onclick="this.href='mai'+'lto:'+this.dataset.name+'@'+this.dataset.domain; return true;"
671                        style="color: var(--secondary-color); text-decoration: none; min-height:44px; display:inline-flex; align-items:center;">
672                        <span class="footer-email" data-name="hello" data-domain="bookyawards.com">Email us</span>
673                    </a>
674                </p>
675            </div>
676
677            <div class="col-lg-3 col-md-6 mb-4">
678                <h2 class="mb-3" style="font-family: var(--font-display); font-weight: 700; color: var(--mm-footer-text); font-size: 1.5rem;">Quick Links</h2>
679                <ul style="list-style: none; padding: 0; margin: 0; line-height: 2.2;">
680                    <li><i class="bi bi-chevron-right" style="color: var(--secondary-color); font-size: 14px;"></i> <a href="/" style="color: var(--mm-footer-text); text-decoration: none; min-height:44px; display:inline-flex; align-items:center;">Home</a></li>
681                    <li><i class="bi bi-chevron-right" style="color: var(--secondary-color); font-size: 14px;"></i> <a href="/blog" style="color: var(--mm-footer-text); text-decoration: none; min-height:44px; display:inline-flex; align-items:center;">Blog</a></li>
682                    <li><i class="bi bi-chevron-right" style="color: var(--secondary-color); font-size: 14px;"></i> <a href="/faq" style="color: var(--mm-footer-text); text-decoration: none; min-height:44px; display:inline-flex; align-items:center;">FAQ</a></li>
683                    <li><i class="bi bi-chevron-right" style="color: var(--secondary-color); font-size: 14px;"></i> <a href="/privacy" style="color: var(--mm-footer-text); text-decoration: none; min-height:44px; display:inline-flex; align-items:center;">Privacy Policy</a></li>
684                    <li><i class="bi bi-chevron-right" style="color: var(--secondary-color); font-size: 14px;"></i> <a href="/terms" style="color: var(--mm-footer-text); text-decoration: none; min-height:44px; display:inline-flex; align-items:center;">Terms of Service</a></li>
685                    
686                </ul>
687            </div>
688
689            <div class="col-lg-3 col-md-6 mb-4">
690                <h2 class="mb-3" style="font-family: var(--font-display); font-weight: 700; color: var(--mm-footer-text); font-size: 1.5rem;">Archieboy Network</h2>
691                <div style="background: rgba(255,255,255,0.05); border: 1px solid rgba(255,255,255,0.1); border-radius: 4px; padding: 14px;">
692                    <a href="https://www.archieboy.com/" target="_blank" rel="noopener" aria-label="Visit Archieboy Holdings" style="min-height:44px; display:inline-flex; align-items:center;">
693                        <img src="https://www.archieboy.com/static/img/logo.png" alt="Archieboy Holdings, LLC" loading="lazy" decoding="async" style="width: 190px; max-width: 100%; height: auto; display: block; margin-bottom: 12px;">
694                    </a>
695                    <p style="line-height: 1.7; margin: 0; color: var(--mm-footer-text);">
696                        This site is part of the
697                        <a href="https://www.archieboy.com/" target="_blank" rel="noopener" style="color: var(--secondary-color); text-decoration: none; min-height:44px; display:inline-flex; align-items:center;">Archieboy Holdings, LLC</a>
698                        network of websites.
699
700
701 To become an affiliate of this website and dozens more in our network, visit
702<a href="https://www.archieboy.com/affiliate/" target="_blank" rel="noopener" style="color: var(--secondary-color); text-decoration: none;">Our Affiliate Program Page</a>.
703
704                    </p>
705                </div>
706            </div>
707
708            <div class="col-lg-3 col-md-6 mb-4">
709                <h2 class="mb-3" style="font-family: var(--font-display); font-weight: 700; color: var(--mm-footer-text); font-size: 1.5rem;">Latest Updates</h2>
710                <div style="background: rgba(255,255,255,0.05); border: 1px solid rgba(255,255,255,0.1); border-radius: 4px; padding: 12px; max-height: 150px; overflow-y: auto;">
711                    <ul id="updates-list" style="list-style: none; padding: 0; margin: 0; color: var(--mm-footer-text);">
712                        
713                        <li style="margin-bottom: 10px; position: relative;" data-id="18">
714                            <span style="font-weight: bold; color: var(--secondary-color);">&#8226; 2026-09-20:</span> Winner pages now highlight a second qualifying craft strength when it scores 75 or higher.
715                            
716                        </li>
717                        
718                        <li style="margin-bottom: 10px; position: relative;" data-id="17">
719                            <span style="font-weight: bold; color: var(--secondary-color);">&#8226; 2026-09-20:</span> Winners can now add or change the retailer link behind the “Get the book” button on their award page.
720                            
721                        </li>
722                        
723                        <li style="margin-bottom: 10px; position: relative;" data-id="16">
724                            <span style="font-weight: bold; color: var(--secondary-color);">&#8226; 2026-09-18:</span> Eligible promotion codes can now be entered directly during checkout for Booky Award and Author Bundle purchases.
725                            
726                        </li>
727                        
728                        <li style="margin-bottom: 10px; position: relative;" data-id="15">
729                            <span style="font-weight: bold; color: var(--secondary-color);">&#8226; 2026-09-14:</span> Free screen uploads are now more reliable, even when larger manuscript files take longer to send.
730                            
731                        </li>
732                        
733                        <li style="margin-bottom: 10px; position: relative;" data-id="14">
734                            <span style="font-weight: bold; color: var(--secondary-color);">&#8226; 2026-09-12:</span> Passed the free screen? You can now revisit your strongest craft area and projected score in My Bookys before entering.
735                            
736                        </li>
737                        
738                    </ul>
739                    
740                </div>
741            </div>
742        </div>
743    </div>
744
745    <div class="container text-center mt-4 pt-3" style="border-top: 1px solid rgba(255,255,255,0.1);">
746        <p class="mb-0" style="color: rgba(255,255,255,.65); font-family: var(--font-body);">
747            &copy; <a href="/auth.html" style="color: rgba(255,255,255,.65); text-decoration: none; min-height:44px; display:inline-flex; align-items:center;">Copyright</a> 2026, <strong><a href="https://www.archieboy.com" style="color: rgba(255,255,255,.65); text-decoration: none; min-height:44px; display:inline-flex; align-items:center;" target="_blank">Archieboy Holdings, LLC.</a></strong> All Rights Reserved.
748        </p>
749    </div>
750</footer>
751
752<script>
753document.addEventListener('DOMContentLoaded', function() {
754    document.querySelectorAll('.footer-email').forEach(function(el) {
755        var n = el.dataset.name, d = el.dataset.domain;
756        el.textContent = n + '\u0040' + d;
757    });
758});
759</script>
759
760
761
762
763
764
765
766<div id="hcwf-lead-capture-modal" class="hcwf-lc-overlay" aria-hidden="true" role="dialog" aria-labelledby="hcwf-lc-headline">
767  <div class="hcwf-lc-dialog hcwf-lc-has-cover" role="document">
768    <button type="button" class="hcwf-lc-close" aria-label="Close" data-action="dismiss">&times;</button>
769
770    
771    <div class="hcwf-lc-cover" aria-hidden="true">
772      <img src="https://www.bookyawards.com/static/lead-magnet-anatomy-of-an-award-worthy-book-cover.png"
773           srcset="https://www.bookyawards.com/static/lead-magnet-anatomy-of-an-award-worthy-book-cover.png 360w"
774           sizes="(max-width: 720px) 180px, 240px"
775           alt="Free guide: Anatomy of an Award-Worthy Book cover"
776           loading="lazy"
777           decoding="async">
778    </div>
779    
780
781    <div class="hcwf-lc-content">
782      <div class="hcwf-lc-body" data-state="form">
783        <h3 id="hcwf-lc-headline" class="hcwf-lc-headline">Free guide: Anatomy of an Award-Worthy Book</h3>
784        <p class="hcwf-lc-subheadline">The 10 dimensions every book-award judge evaluates — and how to honestly score your own manuscript against each.</p>
785        <form id="hcwf-lc-form" novalidate>
786          <div class="hcwf-lc-input-row">
787            <input type="email" name="email" id="hcwf-lc-email" placeholder="[email protected]"
788                   required autocomplete="email" inputmode="email" />
789            <button type="submit" class="hcwf-lc-submit">Send me the guide</button>
790          </div>
791          
792          <div class="hcwf-lc-hp" aria-hidden="true">
793            <input type="text" name="website" tabindex="-1" autocomplete="off" value="" />
794          </div>
795          <div class="hcwf-lc-error" role="alert" aria-live="polite"></div>
796          <p class="hcwf-lc-login">
797            Already a member? <a href="/auth.html">Log in &rarr;</a>
798          </p>
799        </form>
800      </div>
801
802      <div class="hcwf-lc-body" data-state="success" hidden>
803        <div class="hcwf-lc-success-icon" aria-hidden="true">&#10003;</div>
804        <h3 class="hcwf-lc-headline">You're all set!</h3>
805        <p class="hcwf-lc-subheadline" data-success-message>Check your email — your award-worthy book scorecard is on the way, plus a link to set up your free account.</p>
806        <button type="button" class="hcwf-lc-submit" data-action="dismiss">Close</button>
807      </div>
808    </div>
809  </div>
810</div>
811
812<style>
813  .hcwf-lc-overlay {
814    position: fixed; inset: 0; background: rgba(15, 23, 42, 0.65);
815    display: none; align-items: center; justify-content: center;
816    z-index: 10050; padding: 16px;
817    animation: hcwf-lc-fade 0.2s ease-out;
818  }
819  .hcwf-lc-overlay.open { display: flex; }
820  @keyframes hcwf-lc-fade { from { opacity: 0; } to { opacity: 1; } }
821  .hcwf-lc-dialog {
822    background: #ffffff; color: #0f172a; border-radius: 12px;
823    max-width: 480px; width: 100%; padding: 32px 28px;
824    box-shadow: 0 25px 50px -12px rgba(0,0,0,0.4);
825    position: relative;
826    font-family: var(--font-body, system-ui, sans-serif);
827    animation: hcwf-lc-pop 0.25s ease-out;
828  }
829  .hcwf-lc-dialog.hcwf-lc-has-cover {
830    max-width: 720px;
831    padding: 0;
832    display: flex; align-items: stretch;
833    overflow: hidden;
834  }
835  .hcwf-lc-cover {
836    flex: 0 0 240px;
837    background: linear-gradient(135deg, #f3f5fa 0%, #e6ebf3 100%);
838    display: flex; align-items: center; justify-content: center;
839    padding: 24px 18px;
840    border-right: 1px solid rgba(15,23,42,0.06);
841  }
842  .hcwf-lc-cover img {
843    max-width: 100%; max-height: 320px; width: auto; height: auto;
844    object-fit: contain;
845    box-shadow: 0 12px 28px -8px rgba(0,0,0,0.35);
846    border-radius: 4px;
847  }
848  .hcwf-lc-content { padding: 32px 28px; flex: 1 1 auto; min-width: 0; }
849  .hcwf-lc-dialog:not(.hcwf-lc-has-cover) .hcwf-lc-content { padding: 0; }
850  @keyframes hcwf-lc-pop {
851    from { transform: scale(0.94); opacity: 0; }
852    to { transform: scale(1); opacity: 1; }
853  }
854  .hcwf-lc-close {
855    position: absolute; top: 8px; right: 12px;
856    background: none; border: 0; font-size: 28px; line-height: 1;
857    color: #6b7280; cursor: pointer; padding: 4px 10px;
858  }
859  .hcwf-lc-close:hover { color: #0f172a; }
860  .hcwf-lc-headline {
861    font-family: var(--font-display, var(--font-body, system-ui, sans-serif));
862    font-size: 22px; font-weight: 700; margin: 0 0 8px; line-height: 1.25;
863    color: #0f172a;
864  }
865  .hcwf-lc-subheadline { font-size: 15px; color: #475569; margin: 0 0 20px; line-height: 1.5; }
866  .hcwf-lc-input-row { display: flex; gap: 8px; flex-wrap: wrap; }
867  .hcwf-lc-input-row input[type=email] {
868    flex: 1 1 220px; min-width: 0;
869    padding: 12px 14px; font-size: 15px;
870    border: 1px solid #cbd5e1; border-radius: 6px;
871    background: #ffffff; color: #0f172a;
872  }
873  .hcwf-lc-input-row input[type=email]:focus {
874    outline: none; border-color: var(--brand-color, #2c3e50);
875    box-shadow: 0 0 0 3px rgba(44,62,80,0.15);
876  }
877  .hcwf-lc-submit {
878    padding: 12px 22px; font-size: 15px; font-weight: 600;
879    background: var(--brand-color, #2c3e50); color: #ffffff;
880    border: 0; border-radius: 6px; cursor: pointer;
881    transition: filter 0.15s ease;
882  }
883  .hcwf-lc-submit:hover { filter: brightness(1.1); }
884  .hcwf-lc-submit:disabled { opacity: 0.6; cursor: wait; }
885  .hcwf-lc-error { color: #b91c1c; font-size: 14px; min-height: 20px; margin-top: 8px; }
886  .hcwf-lc-hp { position: absolute; left: -9999px; top: -9999px; height: 0; width: 0; overflow: hidden; }
887  .hcwf-lc-login { margin: 14px 0 0; font-size: 14px; color: #475569; }
888  .hcwf-lc-login a { color: var(--brand-color, #2c3e50); font-weight: 600; text-decoration: none; }
889  .hcwf-lc-login a:hover { text-decoration: underline; }
890  .hcwf-lc-success-icon {
891    width: 56px; height: 56px; border-radius: 50%;
892    background: #10b981; color: #ffffff;
893    display: flex; align-items: center; justify-content: center;
894    font-size: 30px; margin: 0 auto 16px;
895  }
896  .hcwf-lc-body[data-state="success"] { text-align: center; }
897  @media (max-width: 720px) {
898    .hcwf-lc-dialog.hcwf-lc-has-cover {
899      flex-direction: column; max-width: 480px;
900    }
901    .hcwf-lc-cover {
902      flex: 0 0 auto; padding: 18px 18px 0;
903      background: #ffffff; border-right: 0;
904    }
905    .hcwf-lc-cover img { max-height: 180px; }
906  }
907  @media (max-width: 480px) {
908    .hcwf-lc-dialog { padding: 24px 20px; }
909    .hcwf-lc-dialog.hcwf-lc-has-cover { padding: 0; }
910    .hcwf-lc-content { padding: 20px 20px 24px; }
911    .hcwf-lc-headline { font-size: 19px; }
912    .hcwf-lc-cover img { max-height: 140px; }
913  }
914</style>
915
916<script>
917(function() {
918  'use strict';
919  var DISMISS_DAYS = 3;
920  var STORAGE_KEY = 'hcwf_lc_dismissed_until';
921  var SUBMITTED_KEY = 'hcwf_lc_submitted';
922  var SCROLL_THRESHOLD = 0.5;
923  var TIME_DELAY_MS = 30000;
924
925  function dismissedUntil() {
926    try {
927      var v = localStorage.getItem(STORAGE_KEY);
928      return v ? parseInt(v, 10) : 0;
929    } catch (_) { return 0; }
930  }
931  function setDismissedUntil() {
932    try {
933      var until = Date.now() + (DISMISS_DAYS * 24 * 60 * 60 * 1000);
934      localStorage.setItem(STORAGE_KEY, String(until));
935    } catch (_) {}
936  }
937  function alreadySubmitted() {
938    try { return !!localStorage.getItem(SUBMITTED_KEY); } catch (_) { return false; }
939  }
940  function markSubmitted() {
941    try { localStorage.setItem(SUBMITTED_KEY, '1'); } catch (_) {}
942  }
943
944  var isPreview = false;
945  try {
946    isPreview = (new URLSearchParams(window.location.search).get('preview_lead_capture') === '1');
947  } catch (_) {}
948
949  if (!isPreview) {
950    if (alreadySubmitted()) return;
951    if (dismissedUntil() > Date.now()) return;
952  }
953
954  var modal = document.getElementById('hcwf-lead-capture-modal');
955  if (!modal) return;
956  var formBody = modal.querySelector('[data-state="form"]');
957  var successBody = modal.querySelector('[data-state="success"]');
958  var form = modal.querySelector('#hcwf-lc-form');
959  var emailInput = modal.querySelector('#hcwf-lc-email');
960  var errorBox = modal.querySelector('.hcwf-lc-error');
961  var submitBtn = modal.querySelector('.hcwf-lc-submit');
962  var opened = false;
963
964  function open() {
965    if (opened) return;
966    opened = true;
967    modal.classList.add('open');
968    modal.setAttribute('aria-hidden', 'false');
969    setTimeout(function() { try { emailInput.focus(); } catch (_) {} }, 50);
970  }
971  function dismiss() {
972    modal.classList.remove('open');
973    modal.setAttribute('aria-hidden', 'true');
974    if (!isPreview && formBody.hasAttribute('hidden') === false) {
975      setDismissedUntil();
976    }
977  }
978
979  // Triggers: scroll 50%, 30s timeout, exit-intent
980  var scrollHandler = function() {
981    var doc = document.documentElement;
982    var max = (doc.scrollHeight - doc.clientHeight);
983    if (max <= 0) return;
984    var pct = (doc.scrollTop || document.body.scrollTop) / max;
985    if (pct >= SCROLL_THRESHOLD) {
986      open();
987      cleanup();
988    }
989  };
990  var exitHandler = function(e) {
991    if (e.clientY <= 0 || e.relatedTarget === null) {
992      open();
993      cleanup();
994    }
995  };
996  var timeoutId = setTimeout(function() { open(); cleanup(); }, TIME_DELAY_MS);
997  window.addEventListener('scroll', scrollHandler, { passive: true });
998  document.addEventListener('mouseout', exitHandler);
999  function cleanup() {
1000    clearTimeout(timeoutId);
1001    window.removeEventListener('scroll', scrollHandler);
1002    document.removeEventListener('mouseout', exitHandler);
1003  }
1004
1005  // Preview mode: open the modal immediately, skip dismissal-persist on close.
1006  if (isPreview) {
1007    open();
1008    cleanup();
1009  }
1010
1011  // Dismiss handlers
1012  modal.addEventListener('click', function(e) {
1013    if (e.target === modal) { dismiss(); return; }
1014    if (e.target.matches('[data-action="dismiss"]')) { dismiss(); }
1015  });
1016  document.addEventListener('keydown', function(e) {
1017    if (e.key === 'Escape' && modal.classList.contains('open')) dismiss();
1018  });
1019
1020  // Submit
1021  form.addEventListener('submit', function(e) {
1022    e.preventDefault();
1023    errorBox.textContent = '';
1024    var email = (emailInput.value || '').trim();
1025    if (!email || email.indexOf('@') < 0) {
1026      errorBox.textContent = 'Please enter a valid email address.';
1027      return;
1028    }
1029    submitBtn.disabled = true;
1030    submitBtn.dataset.originalText = submitBtn.textContent;
1031    submitBtn.textContent = 'Sending…';
1032
1033    fetch('/api/lead-capture', {
1034      method: 'POST',
1035      headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' },
1036      credentials: 'same-origin',
1037      body: JSON.stringify({
1038        email: email,
1039        website: (form.querySelector('input[name="website"]') || { value: '' }).value,
1040        'cf-turnstile-response': (form.querySelector('[name="cf-turnstile-response"]') || { value: '' }).value,
1041        page_url: window.location.href,
1042        campaign: new URLSearchParams(window.location.search).get('utm_campaign') || ''
1043      })
1044    }).then(function(r) {
1045      return r.json().then(function(data) { return { ok: r.ok, data: data }; });
1046    }).then(function(res) {
1047      if (!res.ok) {
1048        errorBox.textContent = (res.data && res.data.error) || 'Something went wrong. Please try again.';
1049        submitBtn.disabled = false;
1050        submitBtn.textContent = submitBtn.dataset.originalText;
1051        return;
1052      }
1053      markSubmitted();
1054      if (res.data && res.data.magnet_url) {
1055        var msgEl = successBody.querySelector('[data-success-message]');
1056        if (msgEl) {
1057          msgEl.innerHTML = (res.data.success_message || msgEl.textContent) +
1058            ' <br><br><a href="' + res.data.magnet_url + '" target="_blank" rel="noopener" style="color:var(--brand-color,#2c3e50);font-weight:600;">Grab it now &rarr;</a>';
1059        }
1060      } else if (res.data && res.data.success_message) {
1061        var msgEl2 = successBody.querySelector('[data-success-message]');
1062        if (msgEl2) msgEl2.textContent = res.data.success_message;
1063      }
1064      formBody.setAttribute('hidden', '');
1065      successBody.removeAttribute('hidden');
1066    }).catch(function() {
1067      errorBox.textContent = 'Network error. Please try again.';
1068      submitBtn.disabled = false;
1069      submitBtn.textContent = submitBtn.dataset.originalText;
1070    });
1071  });
1072})();
1073</script>
1073
1074
1075
1076  
1076<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script>
1076
1077  
1077<script src="/static/js/checkout-guard.js?v=20260930a" defer></script>
1077
1078  
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090<div class="riley-dock" id="rileyDock">
1091  <button type="button" class="riley-launch" id="rileyLaunch"
1092          aria-expanded="false" aria-controls="rileyWidget"
1093          
1094          aria-label="Questions? Ask Riley">
1095    <img src="/riley/static/riley-96.jpg" alt="" width="34" height="34" aria-hidden="true">
1096    <span class="riley-launch-label">Questions? Ask Riley</span>
1097  </button>
1098
1099<div class="riley-widget riley-engaged riley-floating" id="rileyWidget"
1100     hidden
1101     style="--riley-h: 480px;">
1102
1103  <div class="riley-head">
1104    <img class="riley-face" src="/riley/static/riley-96.jpg" alt="Riley" width="38" height="38">
1105    <div class="riley-who">
1106      <strong>Riley</strong>
1107      <span class="riley-sub" id="rileyStatus">Support &middot; AI agent</span>
1108    </div>
1109    <button type="button" class="riley-mode" id="rileyModeBtn"
1110            title="Switch to voice" aria-label="Talk with Riley">
1111      <span class="riley-mode-ico" aria-hidden="true"></span>
1112      <span id="rileyModeLbl">Talk</span>
1113    </button>
1114    
1115    <button type="button" class="riley-close" id="rileyClose"
1116            title="Close" aria-label="Close chat">&times;</button>
1117    
1118  </div>
1119
1120  <div class="riley-body">
1121    <div class="riley-log" id="rileyLog" role="log" aria-live="polite"></div>
1122
1123    
1124    
1125  </div>
1126
1127  <form class="riley-input" id="rileyForm" autocomplete="off">
1128    <input type="text" id="rileyText" maxlength="1200"
1129           placeholder="Ask me anything&hellip;" aria-label="Message to Riley">
1130    <button type="submit" id="rileySend" aria-label="Send">&#8593;</button>
1131  </form>
1132
1133  <p class="riley-foot">
1134    Riley is an AI agent. She can look things up, email you details, and pass
1135    anything she can&rsquo;t finish to a person.
1136  </p>
1137</div>
1138
1139</div>
1140
1141
1142
1143<style>
1144/* Riley takes her colours from the site she is standing on.
1145 *
1146 * Every HCWF site injects a palette into :root at render time, and the pair we
1147 * build on is --mm-navbar-bg / --mm-navbar-text. That pair is already a
1148 * validated dark surface with a legible foreground on each site — it is what
1149 * the header and footer use — so deriving from it keeps the widget on-brand
1150 * without anyone hand-picking a colour per site. A site with a LIGHT navbar
1151 * gets a light widget for the same reason, and it still reads correctly,
1152 * because the foreground travels with the background.
1153 *
1154 * Everything else — muted text, bubbles, borders, the input well — is mixed
1155 * from those two, so there are no third-party colours to drift out of step.
1156 * Each mix has a plain rgba fallback declared first for anything too old to
1157 * know color-mix().
1158 *
1159 * Override per include with .
1160 */
1161.riley-widget {
1162  --rw-surface: var(--mm-navbar-bg, var(--primary-color, #1F3B5B));
1163  --rw-text: var(--mm-navbar-text, #ffffff);
1164  --rw-accent: var(--mm-accent, var(--secondary-color, #F2DFAD));
1165  --rw-accent-text: var(--mm-accent-text, #213547);
1166
1167  display: flex; flex-direction: column;
1168  height: var(--riley-h, 480px);
1169  background: var(--rw-surface);
1170  color: var(--rw-text);
1171  border: 1px solid rgba(255,255,255,.16);
1172  border: 1px solid color-mix(in srgb, var(--rw-text) 16%, transparent);
1173  border-radius: 14px;
1174  box-shadow: 0 14px 38px rgba(0,0,0,.28);
1175  overflow: hidden; font-size: 15px; line-height: 1.45;
1176  font-family: var(--font-body, inherit);
1177}
1178/* Light variant: swap only the surface pair — the rest re-derives itself. */
1179.riley-widget.riley-light {
1180  --rw-surface: var(--surface-bg, #ffffff);
1181  --rw-text: var(--text-dark, #213547);
1182  box-shadow: 0 10px 30px rgba(0,0,0,.10);
1183}
1184
1185.riley-head {
1186  display: flex; align-items: center; gap: 10px;
1187  padding: 12px 14px; flex: 0 0 auto;
1188  border-bottom: 1px solid rgba(255,255,255,.12);
1189  border-bottom: 1px solid color-mix(in srgb, var(--rw-text) 12%, transparent);
1190  background: rgba(0,0,0,.12);
1191  background: color-mix(in srgb, var(--rw-surface) 88%, #000);
1192}
1193.riley-face { width: 38px; height: 38px; border-radius: 50%; object-fit: cover; flex: 0 0 auto; }
1194.riley-who { display: flex; flex-direction: column; line-height: 1.2; min-width: 0; }
1195.riley-who strong { font-size: 15px; color: var(--rw-text); }
1196.riley-sub {
1197  font-size: .875rem;
1198  color: rgba(255,255,255,.70);
1199  color: color-mix(in srgb, var(--rw-text) 70%, transparent);
1200}
1201.riley-mode {
1202  margin-left: auto; display: inline-flex; align-items: center; gap: 6px;
1203  border-radius: 999px; padding: 6px 12px; font-size: .875rem; font-weight: 700;
1204  cursor: pointer; white-space: nowrap;
1205  background: var(--rw-accent); color: var(--rw-accent-text); border: 0;
1206}
1207.riley-mode:hover { filter: brightness(1.06); }
1208.riley-mode.is-live { background: #c0392b; color: #fff; }
1209.riley-mode-ico {
1210  display: inline-block; width: .5625rem; height: .5625rem; border-radius: 50%;
1211  background: currentColor; flex: 0 0 auto;
1212}
1213.riley-mode.is-live .riley-mode-ico { animation: riley-pulse 1.3s ease-in-out infinite; }
1214@keyframes riley-pulse { 0%,100% { opacity: 1 } 50% { opacity: .25 } }
1215
1216/* The log and the landing screen share this box; the landing sits on top. */
1217.riley-body { position: relative; flex: 1 1 auto; min-height: 0; }
1218.riley-log {
1219  position: absolute; inset: 0; overflow-y: auto; padding: 14px;
1220  display: flex; flex-direction: column; gap: 10px; overscroll-behavior: contain;
1221}
1222.riley-msg { max-width: 88%; padding: 9px 12px; border-radius: 13px; white-space: pre-wrap;
1223             word-wrap: break-word; }
1224.riley-msg.riley-them {
1225  background: rgba(255,255,255,.11);
1226  background: color-mix(in srgb, var(--rw-text) 11%, transparent);
1227  color: var(--rw-text);
1228  border-bottom-left-radius: 4px; align-self: flex-start;
1229}
1230/* The visitor's own words wear the site's accent — on SPP that is the same
1231   gold as the primary call to action. */
1232.riley-msg.riley-me {
1233  background: var(--rw-accent); color: var(--rw-accent-text);
1234  border-bottom-right-radius: 4px; align-self: flex-end;
1235}
1236.riley-msg.riley-note {
1237  background: none; font-size: .875rem; font-style: italic;
1238  color: rgba(255,255,255,.62);
1239  color: color-mix(in srgb, var(--rw-text) 62%, transparent);
1240  align-self: center; text-align: center; max-width: 100%; padding: 2px 0;
1241}
1242.riley-typing { display: inline-flex; gap: 4px; align-items: center; }
1243.riley-typing i {
1244  width: 6px; height: 6px; border-radius: 50%;
1245  background: rgba(255,255,255,.65);
1246  background: color-mix(in srgb, var(--rw-text) 65%, transparent);
1247  animation: riley-bounce 1.1s infinite;
1248}
1249.riley-typing i:nth-child(2) { animation-delay: .15s }
1250.riley-typing i:nth-child(3) { animation-delay: .3s }
1251@keyframes riley-bounce { 0%,60%,100% { transform: translateY(0); opacity:.5 }
1252                          30% { transform: translateY(-4px); opacity:1 } }
1253
1254/* Everything here is sized to survive the shortest widget we ship (480px)
1255   without the microphone disclosure being pushed under the footer. Keep the
1256   total under roughly 370px if you change it. */
1257.riley-landing {
1258  position: absolute; inset: 0; background: var(--rw-surface); z-index: 2;
1259  display: flex; flex-direction: column; align-items: center; justify-content: center;
1260  text-align: center; padding: 14px 20px; overflow-y: auto;
1261}
1262.riley-portrait {
1263  width: 112px; height: 112px; border-radius: 50%; object-fit: cover;
1264  box-shadow: 0 6px 22px rgba(0,0,0,.35); flex: 0 0 auto;
1265  border: 2px solid rgba(255,255,255,.22);
1266  border: 2px solid color-mix(in srgb, var(--rw-text) 22%, transparent);
1267}
1268.riley-landing-title {
1269  font-size: 20px; font-weight: 700; margin: 11px 0 0; color: var(--rw-text);
1270  font-family: var(--font-display, inherit);
1271}
1272.riley-landing-copy {
1273  font-size: 14px; margin: 6px 0 0; max-width: 34ch;
1274  color: rgba(255,255,255,.78);
1275  color: color-mix(in srgb, var(--rw-text) 78%, transparent);
1276}
1277.riley-landing-cta {
1278  margin-top: 13px; border: 0; border-radius: 999px; cursor: pointer;
1279  background: var(--rw-accent); color: var(--rw-accent-text);
1280  font-weight: 700; font-size: 15.5px; padding: 12px 28px;
1281  box-shadow: 0 4px 16px rgba(0,0,0,.28);
1282}
1283.riley-landing-cta:hover { filter: brightness(1.06); }
1284.riley-landing-alt {
1285  margin-top: 9px; background: none; border: 0; cursor: pointer;
1286  font-size: 13.5px; text-decoration: underline; padding: 3px;
1287  color: rgba(255,255,255,.80);
1288  color: color-mix(in srgb, var(--rw-text) 80%, transparent);
1289}
1290.riley-landing-note {
1291  font-size: 11px; line-height: 1.35; margin: 11px 0 0; max-width: 40ch;
1292  color: rgba(255,255,255,.62);
1293  color: color-mix(in srgb, var(--rw-text) 62%, transparent);
1294}
1295.riley-widget.riley-engaged .riley-landing { display: none; }
1296
1297.riley-input {
1298  display: flex; gap: 8px; padding: 10px; flex: 0 0 auto;
1299  border-top: 1px solid rgba(255,255,255,.12);
1300  border-top: 1px solid color-mix(in srgb, var(--rw-text) 12%, transparent);
1301  background: rgba(0,0,0,.10);
1302  background: color-mix(in srgb, var(--rw-surface) 92%, #000);
1303}
1304.riley-input input {
1305  flex: 1 1 auto; border-radius: 999px; padding: 10px 14px;
1306  font: inherit; font-size: 15px; min-width: 0;
1307  color: var(--rw-text);
1308  background: rgba(255,255,255,.10);
1309  background: color-mix(in srgb, var(--rw-text) 10%, transparent);
1310  border: 1px solid rgba(255,255,255,.22);
1311  border: 1px solid color-mix(in srgb, var(--rw-text) 22%, transparent);
1312}
1313.riley-input input::placeholder {
1314  color: rgba(255,255,255,.55);
1315  color: color-mix(in srgb, var(--rw-text) 55%, transparent);
1316}
1317.riley-input input:focus { outline: 2px solid var(--rw-accent); outline-offset: -1px; }
1318.riley-input button {
1319  flex: 0 0 auto; width: 40px; height: 40px; border-radius: 50%; border: 0;
1320  background: var(--rw-accent); color: var(--rw-accent-text);
1321  font-size: 17px; font-weight: 700; cursor: pointer;
1322}
1323.riley-input button:disabled { opacity: .45; cursor: default; }
1324.riley-foot {
1325  margin: 0; padding: 8px 14px 11px; font-size: .875rem; flex: 0 0 auto;
1326  color: rgba(255,255,255,.64);
1327  color: color-mix(in srgb, var(--rw-text) 64%, transparent);
1328  background: rgba(0,0,0,.10);
1329  background: color-mix(in srgb, var(--rw-surface) 92%, #000);
1330}
1331
1332/* Before engagement, and while speaking, the typing row is out of the way. */
1333.riley-widget:not(.riley-engaged) .riley-input,
1334.riley-widget.riley-voice .riley-input { display: none; }
1335.riley-widget:not(.riley-engaged) .riley-mode { visibility: hidden; }
1336
1337/* ── floating launcher ────────────────────────────────────────────────
1338   Fixed bottom-right, above ordinary page furniture but below anything that
1339   deliberately takes over the screen. The panel is a sibling of the button so
1340   the button keeps its place while the panel opens above it. */
1341.riley-dock {
1342  position: fixed; right: 20px; bottom: 20px; z-index: 2147483000;
1343  display: flex; flex-direction: column; align-items: flex-end; gap: 10px;
1344  font-family: var(--font-body, inherit);
1345}
1346.riley-launch {
1347  display: inline-flex; align-items: center; gap: 10px; cursor: pointer;
1348  border: 0; border-radius: 999px; padding: 8px 18px 8px 8px;
1349  background: var(--mm-navbar-bg, var(--primary-color, #1F3B5B));
1350  color: var(--mm-navbar-text, #fff);
1351  box-shadow: 0 8px 26px rgba(0,0,0,.30);
1352  font-size: 15px; font-weight: 600; line-height: 1;
1353  max-width: min(78vw, 320px);
1354}
1355.riley-launch img { border-radius: 50%; object-fit: cover; flex: 0 0 auto; }
1356.riley-launch:hover { filter: brightness(1.08); }
1357.riley-launch-label { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
1358.riley-dock.is-open .riley-launch { display: none; }
1359
1360.riley-widget.riley-floating {
1361  width: min(92vw, 400px);
1362  box-shadow: 0 18px 48px rgba(0,0,0,.34);
1363}
1364.riley-close {
1365  background: none; border: 0; cursor: pointer; font-size: 24px; line-height: 1;
1366  padding: 0 2px; margin-left: 6px;
1367  color: rgba(255,255,255,.75);
1368  color: color-mix(in srgb, var(--rw-text) 75%, transparent);
1369}
1370.riley-close:hover { color: var(--rw-text); }
1371
1372@media (max-width: 575.98px) {
1373  /* On a phone the panel takes the width it needs; the launcher shrinks to
1374     the avatar so it never straddles the screen. */
1375  .riley-dock { right: 12px; bottom: 12px; left: 12px; align-items: flex-end; }
1376  .riley-widget.riley-floating { width: 100%; }
1377  .riley-launch-label { display: none; }
1378  .riley-launch { padding: 8px; }
1379}
1380@media (max-width: 575.98px) {
1381  .riley-widget { height: min(70vh, 480px); font-size: 14.5px; }
1382  .riley-portrait { width: 104px; height: 104px; }
1383  .riley-landing-title { font-size: 19px; margin-top: 10px; }
1384  .riley-landing-note { margin-top: 10px; }
1385}
1386@media (prefers-reduced-motion: reduce) {
1387  .riley-typing i, .riley-mode.is-live .riley-mode-ico { animation: none; }
1388}
1389</style>
1390
1391
1392<script>
1393(function () {
1394  "use strict";
1395  var root = document.getElementById('rileyWidget');
1396  if (!root || root.dataset.booted) { return; }
1397  root.dataset.booted = '1';
1398
1399  var logEl    = document.getElementById('rileyLog');
1400  var formEl   = document.getElementById('rileyForm');
1401  var textEl   = document.getElementById('rileyText');
1402  var sendEl   = document.getElementById('rileySend');
1403  var modeBtn  = document.getElementById('rileyModeBtn');
1404  var modeLbl  = document.getElementById('rileyModeLbl');
1405  var statusEl = document.getElementById('rileyStatus');
1406  var talkCta  = document.getElementById('rileyLandingTalk');
1407  var typeCta  = document.getElementById('rileyLandingType');
1408  var hasLanding = !!document.getElementById('rileyLanding');
1409  var dock      = document.getElementById('rileyDock');
1410  var launchBtn = document.getElementById('rileyLaunch');
1411  var closeBtn  = document.getElementById('rileyClose');
1412  var floating  = !!dock;
1413
1414  // Carried across page loads so the thread survives navigation exactly,
1415  // rather than relying on the server's two-hour resume window.
1416  var STORE_CONV = 'rileyConvId', STORE_OPEN = 'rileyOpen';
1417  function remember(k, v) { try { sessionStorage.setItem(k, v); } catch (e) {} }
1418  function recall(k) { try { return sessionStorage.getItem(k); } catch (e) { return null; } }
1419
1420  var convId = recall('rileyConvId') || null;
1421  var busy = false;
1422  var voice = null;          // live voice session, or null
1423  var connecting = false;    // mint + mic permission in flight
1424  var voiceAttempt = 0;      // generation token; invalidates every older in-flight attempt
1425  var started = false;       // session fetched
1426  var engaged = !hasLanding; // with no landing screen the chat is live at once
1427  var pending = null;        // session payload (held back only if there IS a landing)
1428  var freshVisit = false;    // greeting painted this visit but not yet stored
1429
1430  // ---- rendering ----------------------------------------------------------
1431
1432  function bubble(who, text) {
1433    var el = document.createElement('div');
1434    el.className = 'riley-msg riley-' + who;
1435    el.textContent = text;
1436    logEl.appendChild(el);
1437    logEl.scrollTop = logEl.scrollHeight;
1438    return el;
1439  }
1440
1441  function note(text) { return bubble('note', text); }
1442
1443  function typing() {
1444    var el = document.createElement('div');
1445    el.className = 'riley-msg riley-them';
1446    el.innerHTML = '<span class="riley-typing"><i></i><i></i><i></i></span>';
1447    logEl.appendChild(el);
1448    logEl.scrollTop = logEl.scrollHeight;
1449    return el;
1450  }
1451
1452  function vEvent(what) {
1453    // Voice outcomes, so a session that yields no transcript can be told
1454    // apart from one the visitor never really started.
1455    if (!convId) { return; }
1456    post('/riley/api/log', { conv_id: convId, role: 'event', content: what });
1457  }
1458
1459  function post(path, body) {
1460    return fetch(path, {
1461      method: 'POST',
1462      headers: { 'Content-Type': 'application/json' },
1463      credentials: 'same-origin',
1464      body: JSON.stringify(body || {})
1465    }).then(function (r) { return r.json().catch(function () { return {}; }); });
1466  }
1467
1468  // ---- opening ------------------------------------------------------------
1469  //
1470  // By default Riley says hello as soon as the panel is on screen and the text
1471  // box is ready to type in. The earlier design held everything behind a
1472  // "Talk with Riley" button and nobody pressed it.
1473  //
1474  // A return visit still opens on a clean greeting rather than a wall of last
1475  // week's transcript — she carries the history in her own context, so she
1476  // answers as though she remembers, which she does.
1477
1478  // `carryOver` is true when the panel was ALREADY open and the visitor simply
1479  // moved to another page. Wiping the transcript there would be wrong — they
1480  // are mid-conversation and following it around the site is the whole point
1481  // of the launcher. A genuinely new visit still opens on a clean greeting.
1482  var carryOver = false;
1483
1484  function paintOpening(d) {
1485    var history = d.history || [];
1486    if (carryOver && history.length) {
1487      history.forEach(function (m) {
1488        bubble(m.role === 'user' ? 'me' : 'them', m.content);
1489      });
1490      freshVisit = false;
1491      return;
1492    }
1493    if (d.opening) { bubble('them', d.opening); }
1494    if (history.length) {
1495      note('We have spoken before — I still have our conversation.');
1496    }
1497    freshVisit = true;
1498  }
1499
1500  function boot() {
1501    if (started) { return; }
1502    started = true;
1503    post('/riley/api/session', { page_url: location.href, conv_id: convId })
1504      .then(function (d) {
1505        if (d.error) {
1506          pending = null;
1507          if (!hasLanding) { note('Riley is offline just now.'); }
1508          return;
1509        }
1510        convId = d.conv_id;
1511        remember(STORE_CONV, convId);
1512        pending = d;
1513        if (!hasLanding) { paintOpening(d); }
1514      })
1515      .catch(function () {
1516        pending = null;
1517        if (!hasLanding) { note('Riley is offline just now.'); }
1518      });
1519  }
1520
1521  function engage(mode) {
1522    if (engaged) { return; }
1523    engaged = true;
1524    root.classList.add('riley-engaged');
1525
1526    var d = pending || {};
1527    var history = d.history || [];
1528    if (history.length) {
1529      note('Carrying on from last time.');
1530      history.forEach(function (m) {
1531        bubble(m.role === 'user' ? 'me' : 'them', m.content);
1532      });
1533    } else if (d.opening) {
1534      bubble('them', d.opening);
1535    } else if (!pending) {
1536      note('Riley is offline just now.');
1537      return;
1538    }
1539
1540    if (mode === 'voice') { startVoice(); }
1541    else { textEl.focus(); }
1542  }
1543
1544  if (talkCta) { talkCta.addEventListener('click', function () { engage('voice'); }); }
1545  if (typeCta) { typeCta.addEventListener('click', function () { engage('text'); }); }
1546
1547  // ---- text lane ----------------------------------------------------------
1548
1549  formEl.addEventListener('submit', function (e) {
1550    e.preventDefault();
1551    var msg = (textEl.value || '').trim();
1552    if (!msg || busy) { return; }
1553    textEl.value = '';
1554    bubble('me', msg);
1555    busy = true; sendEl.disabled = true;
1556    var dots = typing();
1557    post('/riley/api/text', { message: msg, conv_id: convId,
1558                              page_url: location.href, fresh_visit: freshVisit })
1559      .then(function (d) {
1560        dots.remove();
1561        if (d.conv_id) { convId = d.conv_id; remember(STORE_CONV, convId); }
1562        bubble('them', d.reply || "Sorry — I didn't catch that. Try again?");
1563      })
1564      .catch(function () {
1565        dots.remove();
1566        bubble('them', 'Sorry, something went wrong on my end. Try that once more?');
1567      })
1568      .finally(function () {
1569        busy = false; sendEl.disabled = false; freshVisit = false; textEl.focus();
1570      });
1571  });
1572
1573  // ---- voice lane: GPT-Live 1 / Gleam --------------------------------------
1574  // WebRTC owns continuous duplex audio. Server sideband owns tools, persisted
1575  // transcripts and billing; this page forwards only observed function IDs.
1576  function setVoiceUI(on, label) {
1577    root.classList.toggle('riley-voice', on);
1578    modeBtn.classList.toggle('is-live', on);
1579    modeLbl.textContent = on ? 'Stop' : 'Talk';
1580    modeBtn.title = on ? 'Switch back to typing' : 'Switch to voice';
1581    modeBtn.setAttribute('aria-label', on ? 'Stop voice chat' : 'Talk with Riley');
1582    statusEl.innerHTML = label || (on ? 'Listening&hellip;' : 'Support &middot; AI agent');
1583  }
1584
1585  function closeVoiceParts(parts, immediately) {
1586    if (!parts || parts.disposed) return;
1587    parts.closing = true;
1588    try { parts.mic && parts.mic.getTracks().forEach(function(t){t.stop();}); } catch(e) {}
1589    try { parts.audio && parts.audio.pause(); if(parts.audio) parts.audio.srcObject=null; } catch(e) {}
1590    if (parts.timer) clearTimeout(parts.timer);
1591    if (parts.meter) clearInterval(parts.meter);
1592    try { parts.audioContext && parts.audioContext.close(); } catch(e) {}
1593    function dispose() {
1594      if(parts.disposed) return;
1595      parts.disposed=true;
1596      clearTimeout(parts.closeTimer);clearTimeout(parts.connectTimer);
1597      try { parts.dc && parts.dc.close(); } catch(e) {}
1598      try { parts.pc && parts.pc.close(); } catch(e) {}
1599    }
1600    parts.dispose=dispose;
1601    if (parts.liveId) {
1602      post('/riley/api/live-close',{conv_id:convId,live_id:parts.liveId}).catch(function(){});
1603    }
1604    if (!immediately && parts.dc && parts.dc.readyState==='open') {
1605      try { parts.dc.send(JSON.stringify({type:'session.close'})); } catch(e) {}
1606      parts.closeTimer=setTimeout(dispose,15000);
1607    } else dispose();
1608  }
1609
1610  function stopVoice(silent) {
1611    ++voiceAttempt; connecting=false;
1612    var old=voice;voice=null;
1613    closeVoiceParts(old);
1614    setVoiceUI(false);
1615    if(old && !silent) note('Back to typing — Riley still has the conversation.');
1616    textEl.focus();
1617  }
1618
1619  function caption(parts, role, msg) {
1620    if(!msg.delta) return;
1621    var entry=parts.captions[role];
1622    if(entry && msg.start_ms!=null && entry.end!=null && msg.start_ms-entry.end>900) entry=null;
1623    if(!entry) {
1624      entry={element:bubble(role==='user'?'me':'them',''),text:'',end:null};
1625      parts.captions[role]=entry;
1626    }
1627    entry.text+=msg.delta;entry.end=msg.end_ms;
1628    entry.element.textContent=entry.text;
1629    logEl.scrollTop=logEl.scrollHeight;
1630  }
1631
1632  function handleEvent(ev,parts) {
1633    var msg;try {msg=JSON.parse(ev.data);}catch(e){return;}
1634    if(msg.type==='session.closed') {
1635      if(parts.dispose) parts.dispose();
1636      else if(voice===parts) stopVoice(true);
1637      return;
1638    }
1639    if(parts.closing || voice!==parts || parts.attempt!==voiceAttempt) return;
1640    if(msg.type==='session.started') {
1641      clearTimeout(parts.connectTimer);parts.ready=true;connecting=false;
1642      setVoiceUI(true,'Listening&hellip;');vEvent('voice_connected:gpt-live-1:gleam');
1643    } else if(msg.type==='session.input_transcript.delta') {
1644      caption(parts,'user',msg);
1645    } else if(msg.type==='session.output_transcript.delta') {
1646      caption(parts,'assistant',msg);
1647    } else if(msg.type==='response.event') {
1648      var event=msg.event||{}, item=event.item||{};
1649      if(event.type==='response.output_item.done' && item.type==='function_call' && !parts.calls[item.call_id]) {
1650        parts.calls[item.call_id]=true;
1651        function forward(retry) {
1652          return post('/riley/api/live-tool',{conv_id:convId,live_id:parts.liveId,call_id:item.call_id})
1653            .then(function(d){
1654              if(!d || !d.ok) {
1655                if(retry && !parts.closing) return new Promise(function(resolve){setTimeout(resolve,500);}).then(function(){return forward(false);});
1656                throw new Error('tool not accepted');
1657              }
1658            });
1659        }
1660        forward(true).catch(function(){
1661          if(voice===parts && !parts.closing) {note('Voice lost its support connection. Please try again or continue typing.');stopVoice(true);}
1662        });
1663      }
1664    } else if(msg.type==='error') {
1665      vEvent('voice_failed:live_command');note('Voice encountered a connection problem. Typing still works.');stopVoice(true);
1666    }
1667  }
1668
1669  function monitorPlayback(parts,stream) {
1670    try {
1671      var Ctx=window.AudioContext||window.webkitAudioContext;
1672      var ctx=parts.audioContext=new Ctx();ctx.resume();
1673      var source=ctx.createMediaStreamSource(stream),analyser=ctx.createAnalyser();
1674      analyser.fftSize=256;source.connect(analyser);var samples=new Float32Array(256);
1675      parts.meter=setInterval(function(){
1676        if(voice!==parts || !parts.ready || parts.closing) return;
1677        analyser.getFloatTimeDomainData(samples);var energy=0;
1678        for(var i=0;i<samples.length;i++) energy+=samples[i]*samples[i];
1679        setVoiceUI(true,Math.sqrt(energy/samples.length)>.007 ? 'Riley is speaking&hellip;' : 'Listening&hellip;');
1680      },150);
1681    } catch(e) {} // Voice itself does not depend on the level indicator.
1682  }
1683
1684  function gatherIce(pc) {
1685    if(pc.iceGatheringState==='complete') return Promise.resolve();
1686    return new Promise(function(resolve,reject){
1687      var timer=setTimeout(function(){pc.removeEventListener('icegatheringstatechange',check);reject(new Error('ICE timeout'));},10000);
1688      function check(){if(pc.iceGatheringState==='complete'){clearTimeout(timer);pc.removeEventListener('icegatheringstatechange',check);resolve();}}
1689      pc.addEventListener('icegatheringstatechange',check);check();
1690    });
1691  }
1692
1693  function startVoice() {
1694    if(voice || connecting) {stopVoice();return;}
1695    if(!navigator.mediaDevices || !window.RTCPeerConnection) {note("This browser can't do voice — typing works fine though.");return;}
1696    connecting=true;var attempt=++voiceAttempt,parts=null;
1697    setVoiceUI(true,'Connecting&hellip;');
1698    navigator.mediaDevices.getUserMedia({audio:{channelCount:1,echoCancellation:true,noiseSuppression:true}})
1699    .then(function(mic){
1700      if(attempt!==voiceAttempt){mic.getTracks().forEach(function(t){t.stop();});throw new Error('aborted');}
1701      var pc=new RTCPeerConnection(),audio=new Audio();audio.autoplay=true;audio.setAttribute('playsinline','');
1702      parts={pc:pc,audio:audio,mic:mic,dc:null,attempt:attempt,captions:{},calls:{},ready:false};voice=parts;
1703      pc.ontrack=function(e){
1704        if(parts.closing)return;
1705        var stream=e.streams[0]||new MediaStream([e.track]);audio.srcObject=stream;
1706        audio.play().catch(function(){if(voice===parts)note('Your browser blocked audio playback. Stop voice and try Talk again.');});
1707        monitorPlayback(parts,stream);
1708      };
1709      pc.onconnectionstatechange=function(){
1710        if(!parts.closing && voice===parts && pc.connectionState==='failed') {note('Voice disconnected. You can continue typing.');stopVoice(true);}
1711      };
1712      mic.getTracks().forEach(function(t){pc.addTrack(t,mic);});
1713      var dc=parts.dc=pc.createDataChannel('oai-events');
1714      dc.onmessage=function(e){handleEvent(e,parts);};
1715      dc.onclose=function(){if(!parts.closing && voice===parts){note('Voice conversation ended.');stopVoice(true);}};
1716      parts.connectTimer=setTimeout(function(){if(voice===parts && !parts.ready){note('Voice could not connect. Please try again.');stopVoice(true);}},45000);
1717      return pc.createOffer().then(function(offer){return pc.setLocalDescription(offer);})
1718        .then(function(){return gatherIce(pc);})
1719        .then(function(){
1720          if(attempt!==voiceAttempt || parts.closing)throw new Error('aborted');
1721          return post('/riley/api/live-session',{conv_id:convId,page_url:location.href,sdp:pc.localDescription.sdp});
1722        }).then(function(d){
1723          if(!d || !d.sdp || !d.live_id)throw new Error((d&&d.detail)||'no voice session');
1724          parts.liveId=d.live_id;
1725          if(attempt!==voiceAttempt || parts.closing) {
1726            post('/riley/api/live-close',{conv_id:d.conv_id||convId,live_id:d.live_id}).catch(function(){});
1727            throw new Error('aborted');
1728          }
1729          convId=d.conv_id||convId;remember(STORE_CONV,convId);
1730          parts.timer=setTimeout(function(){if(voice===parts){note('That is the limit for one voice session — carry on by typing.');stopVoice(true);}},((d.max_seconds||600)-10)*1000);
1731          return pc.setRemoteDescription({type:'answer',sdp:d.sdp});
1732        });
1733    }).catch(function(err){
1734      closeVoiceParts(parts,true);
1735      if(voice===parts)voice=null;
1736      if(attempt!==voiceAttempt || (err&&err.message==='aborted'))return;
1737      connecting=false;setVoiceUI(false);
1738      if(err&&err.name==='NotAllowedError'){vEvent('voice_failed:mic_denied');note('No microphone access — happy to keep going by text.');}
1739      else {vEvent('voice_failed:live_start');note("Couldn't start voice just now. Typing still works.");}
1740      textEl.focus();
1741    });
1742  }
1743
1744  modeBtn.addEventListener('click',startVoice);
1745  window.addEventListener('beforeunload',function(){
1746    if(voice && voice.liveId && navigator.sendBeacon)navigator.sendBeacon('/riley/api/live-close',new Blob([JSON.stringify({conv_id:convId,live_id:voice.liveId})],{type:'application/json'}));
1747    stopVoice(true);
1748  });
1749
1750  // ---- floating launcher --------------------------------------------------
1751  //
1752  // The panel is on every page now, so it must NOT open a conversation just
1753  // because a page loaded — that would create a row per pageview across the
1754  // whole site and drown the very numbers we are trying to read. A
1755  // conversation begins when somebody actually opens the chat.
1756
1757  function openPanel() {
1758    if (!floating) { return; }
1759    dock.classList.add('is-open');
1760    root.hidden = false;
1761    launchBtn.setAttribute('aria-expanded', 'true');
1762    remember(STORE_OPEN, '1');
1763    boot();
1764    if (engaged) { setTimeout(function () { textEl.focus(); }, 60); }
1765  }
1766
1767  function closePanel() {
1768    if (!floating) { return; }
1769    stopVoice(true);
1770    dock.classList.remove('is-open');
1771    root.hidden = true;
1772    launchBtn.setAttribute('aria-expanded', 'false');
1773    remember(STORE_OPEN, '0');
1774    launchBtn.focus();
1775  }
1776
1777  if (floating) {
1778    launchBtn.addEventListener('click', openPanel);
1779    if (closeBtn) { closeBtn.addEventListener('click', closePanel); }
1780    document.addEventListener('keydown', function (e) {
1781      if (e.key === 'Escape' && !root.hidden) { closePanel(); }
1782    });
1783    // Stay open as they move around the site, transcript and all.
1784    if (recall(STORE_OPEN) === '1') { carryOver = true; openPanel(); }
1785  }
1786
1787  // Boot on first sight — inline mode only. Floating mode boots on open.
1788  if (floating) {
1789    /* handled by openPanel */
1790  } else if (root.offsetParent !== null) {
1791    boot();
1792  } else {
1793    var io = new IntersectionObserver(function (entries) {
1794      entries.forEach(function (en) {
1795        if (en.isIntersecting) { boot(); io.disconnect(); }
1796      });
1797    });
1798    io.observe(root);
1799    document.addEventListener('click', function once() {
1800      if (root.offsetParent !== null) { boot(); document.removeEventListener('click', once); }
1801    });
1802  }
1803})();
1804</script>
1804
1805
1806</body>
1807</html>

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.