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'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'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'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);">• 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);">• 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);">• 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);">• 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);">• 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 © <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">×</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 →</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">✓</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 →</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 · 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">×</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…" aria-label="Message to Riley"> 1130 <button type="submit" id="rileySend" aria-label="Send">↑</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’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…' : 'Support · 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…');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…' : 'Listening…'); 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…'); 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.