Sumber pakej · Storybook Sequence Library

CC BY 4.0 · sila beri kredit @joe · v1

← Butiran permainan

Ambil, olah semula, ajar dengannya

Kod di bawah hanyalah HTML masuk pakej yang boleh dimainkan. Muat turun ZIP yang lengkap untuk menyunting atau mengolah semula permainan bersama semua aset relatifnya.

⬇ Muat turun HTML masuk ⬇ Muat turun pakej lengkap
Pemula gesaan suntingan AI
Sunting permainan bilik darjah ini untuk papan putih interaktif. Kekalkan sasaran sentuhan yang besar dan permainan seluruh kelas. Penjejakan, log masuk, pengumpulan data peribadi dan panggilan API jauh adalah dilarang. Gunakan ZIP lengkap yang dimuat turun—bukan hanya HTML masuk yang ditunjukkan di bawah—dan kekalkan struktur foldernya yang sedia untuk pelayar. Kekalkan matlamat pendidikan. Pencipta asal: @joe di ClassBoard Games (CC BY 4.0).
HTML masuk sahaja · index.html · 3518.4 KB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" />
<title>Storybook Sequence Library — FIRST, NEXT, THEN, LAST</title>
<style>
  :root {
    --app-scale: 1;
    --wood-dark: #3B1D0A;
    --wood-mid: #6D3714;
    --wood-light: #9A5220;
    --parchment: #FFFDF5;
    --parchment-warm: #FEF7E0;
    --c-first: #E11D48;
    --c-next: #D97706;
    --c-then: #2563EB;
    --c-last: #059669;
  }

  * {
    box-sizing: border-box;
    user-select: none;
    -webkit-user-select: none;
    -webkit-touch-callout: none;
  }

  html, body {
    margin: 0;
    padding: 0;
    width: 100%;
    height: 100%;
    overflow: hidden;
    font-family: "Nunito", "Fredoka", "Comic Sans MS", "Trebuchet MS", system-ui, -apple-system, sans-serif;
    background: radial-gradient(circle at 50% 20%, #78350F 0%, #3B1D0A 55%, #1C0D04 100%);
    color: #1E293B;
  }

  .viewport-shell {
    width: 100vw;
    height: 100vh;
    display: flex;
    align-items: center;
    justify-content: center;
    overflow: hidden;
    position: relative;
  }

  .stage-1280 {
    width: 1280px;
    height: 720px;
    transform: scale(var(--app-scale));
    transform-origin: center center;
    background: linear-gradient(180deg, #5C2D12 0%, #451A03 100%);
    border-radius: 30px;
    border: 7px solid #D97706;
    box-shadow: 0 26px 70px rgba(0, 0, 0, 0.7), inset 0 2px 0 rgba(254, 243, 199, 0.55);
    display: flex;
    flex-direction: column;
    padding: 11px 15px;
    gap: 8px;
    position: relative;
    overflow: hidden;
  }

  /* Warm Library Wallpaper & Bookshelf Texture */
  .library-backdrop {
    position: absolute;
    inset: 0;
    pointer-events: none;
    background-image:
      radial-gradient(circle at 18% 12%, rgba(251, 191, 36, 0.16) 0%, transparent 36%),
      radial-gradient(circle at 82% 12%, rgba(251, 191, 36, 0.16) 0%, transparent 36%),
      repeating-linear-gradient(90deg, rgba(255,255,255,0.03) 0px, rgba(255,255,255,0.03) 28px, transparent 28px, transparent 56px);
    z-index: 0;
  }

  /* ============ 1. TOP LIBRARY HEADER BAR ============ */
  .top-bar {
    position: relative;
    z-index: 10;
    height: 58px;
    background: linear-gradient(90deg, #78350F 0%, #92400E 50%, #78350F 100%);
    border-radius: 18px;
    border: 3px solid #FBBF24;
    box-shadow: 0 5px 0 #291003;
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 0 14px;
    color: #FFFBEB;
    flex-shrink: 0;
  }

  .brand-cluster {
    display: flex;
    align-items: center;
    gap: 10px;
  }

  .brand-logo {
    width: 43px;
    height: 43px;
    border-radius: 13px;
    background: linear-gradient(135deg, #FDE047, #D97706);
    border: 3px solid #FFFBEB;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 24px;
    box-shadow: 0 3px 0 #451A03;
  }

  .brand-title {
    font-size: 21px;
    font-weight: 900;
    letter-spacing: 0.3px;
    line-height: 1.05;
    text-shadow: 0 2px 4px rgba(0,0,0,0.45);
  }

  .brand-sub {
    font-size: 11px;
    font-weight: 800;
    color: #FDE68A;
  }

  .mode-tabs {
    display: flex;
    align-items: center;
    gap: 6px;
    background: rgba(28, 13, 4, 0.45);
    padding: 4px;
    border-radius: 13px;
    border: 2px solid rgba(253, 224, 71, 0.3);
  }

  .mode-tab {
    border: none;
    background: transparent;
    color: #FEF3C7;
    font-family: inherit;
    font-size: 12.5px;
    font-weight: 900;
    padding: 6px 12px;
    border-radius: 9px;
    cursor: pointer;
    display: flex;
    align-items: center;
    gap: 5px;
    transition: all 0.14s;
  }
  .mode-tab:hover { background: rgba(255,255,255,0.12); }
  .mode-tab.active {
    background: linear-gradient(180deg, #FDE047, #F59E0B);
    color: #451A03;
    box-shadow: 0 3px 0 #92400E;
  }

  .top-right {
    display: flex;
    align-items: center;
    gap: 6px;
  }

  .star-pill {
    background: rgba(28, 13, 4, 0.45);
    border: 2px solid #FDE047;
    border-radius: 999px;
    padding: 4px 11px;
    font-size: 13.5px;
    font-weight: 900;
    color: #FEF08A;
    display: flex;
    align-items: center;
    gap: 4px;
  }

  .util-btn {
    border: 2px solid rgba(254, 243, 199, 0.55);
    background: rgba(255, 255, 255, 0.14);
    color: #FFFBEB;
    font-family: inherit;
    font-size: 12px;
    font-weight: 900;
    padding: 6px 10px;
    border-radius: 10px;
    cursor: pointer;
    display: flex;
    align-items: center;
    gap: 4px;
  }
  .util-btn:hover { background: rgba(255, 255, 255, 0.25); }

  /* ============ 2. SEQUENCE WORDS RIBBON & 10-BOOK SHELF BAR ============ */
  .library-shelf-bar {
    position: relative;
    z-index: 10;
    height: 60px;
    background: linear-gradient(180deg, #FFFBEB 0%, #FEF3C7 100%);
    border-radius: 18px;
    border: 3px solid #F59E0B;
    box-shadow: 0 5px 0 rgba(0, 0, 0, 0.28);
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 0 10px;
    gap: 8px;
    flex-shrink: 0;
  }

  .seq-rule-pills {
    display: flex;
    align-items: center;
    gap: 6px;
  }

  .seq-rule-btn {
    border: 2.5px solid;
    border-radius: 12px;
    padding: 4px 9px;
    cursor: pointer;
    font-family: inherit;
    display: flex;
    align-items: center;
    gap: 6px;
    transition: transform 0.12s;
  }
  .seq-rule-btn:hover { transform: translateY(-2px); }

  .seq-rule-btn.first { background: #FFF1F2; border-color: #FB7185; color: #9F1239; }
  .seq-rule-btn.next  { background: #FFFBEB; border-color: #FBBF24; color: #92400E; }
  .seq-rule-btn.then  { background: #EFF6FF; border-color: #60A5FA; color: #1E40AF; }
  .seq-rule-btn.last  { background: #ECFDF5; border-color: #34D399; color: #065F46; }

  .srb-tag {
    font-size: 13px;
    font-weight: 900;
    padding: 2px 7px;
    border-radius: 7px;
    color: #fff;
  }
  .seq-rule-btn.first .srb-tag { background: var(--c-first); }
  .seq-rule-btn.next .srb-tag  { background: var(--c-next); }
  .seq-rule-btn.then .srb-tag  { background: var(--c-then); }
  .seq-rule-btn.last .srb-tag  { background: var(--c-last); }

  .srb-lbl {
    font-size: 11px;
    font-weight: 900;
    line-height: 1.1;
    text-align: left;
  }
  .srb-lbl small {
    display: block;
    font-size: 9.5px;
    opacity: 0.85;
  }

  /* 10-Book Carousel Picker */
  .book-spines-row {
    display: flex;
    align-items: center;
    gap: 4px;
    overflow-x: auto;
    padding: 2px;
  }

  .book-spine-btn {
    border: 2px solid #D97706;
    background: #FFFFFF;
    color: #78350F;
    font-family: inherit;
    font-size: 11.5px;
    font-weight: 900;
    padding: 5px 8px;
    border-radius: 10px;
    cursor: pointer;
    display: flex;
    align-items: center;
    gap: 4px;
    white-space: nowrap;
    transition: all 0.14s;
  }
  .book-spine-btn:hover { background: #FEF3C7; transform: translateY(-1px); }
  .book-spine-btn.active {
    background: linear-gradient(180deg, #D97706, #92400E);
    border-color: #451A03;
    color: #FFFBEB;
    box-shadow: 0 3px 0 #451A03;
  }

  /* ============ 3. MAIN SPLIT: LEFT LIBRARY READING DESK + RIGHT 3-TEAM BOOK STACK ============ */
  .main-split {
    position: relative;
    z-index: 10;
    flex: 1;
    min-height: 0;
    display: grid;
    grid-template-columns: 1fr 310px;
    gap: 11px;
  }

  .reading-desk {
    background: linear-gradient(180deg, #FFFDF5 0%, #FEF7E0 100%);
    border-radius: 24px;
    border: 5px solid #B45309;
    box-shadow: 0 12px 28px rgba(0, 0, 0, 0.35), inset 0 2px 0 #FFFFFF;
    padding: 10px 14px;
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    position: relative;
    overflow: hidden;
  }

  .desk-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    padding-bottom: 6px;
    border-bottom: 2.5px dashed #FCD34D;
    flex-shrink: 0;
  }

  .book-banner-title {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 18px;
    font-weight: 900;
    color: #451A03;
  }

  .desk-actions {
    display: flex;
    align-items: center;
    gap: 6px;
  }

  .lib-btn {
    border: 2.5px solid #D97706;
    background: linear-gradient(180deg, #FEF08A, #FBBF24);
    color: #451A03;
    font-family: inherit;
    font-size: 12px;
    font-weight: 900;
    padding: 5px 11px;
    border-radius: 11px;
    box-shadow: 0 3px 0 #92400E;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    gap: 5px;
  }
  .lib-btn:active { transform: translateY(2px); box-shadow: 0 1px 0 #92400E; }
  .lib-btn.green {
    border-color: #059669;
    background: linear-gradient(180deg, #A7F3D0, #34D399);
    color: #064E3B;
    box-shadow: 0 3px 0 #065F46;
  }
  .lib-btn.purple {
    border-color: #6D28D9;
    background: linear-gradient(180deg, #DDD6FE, #A78BFA);
    color: #2E1065;
    box-shadow: 0 3px 0 #4C1D95;
  }

  /* SHUFFLED BOOKMARK TRAY (Mode 1 & Mode 2) */
  .bookmark-tray {
    background: linear-gradient(90deg, #78350F 0%, #92400E 50%, #78350F 100%);
    border: 3px solid #F59E0B;
    border-radius: 18px;
    padding: 8px 12px;
    margin: 6px 0;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    flex-shrink: 0;
    box-shadow: inset 0 3px 8px rgba(0,0,0,0.35);
  }

  .tray-instruction {
    font-size: 12.5px;
    font-weight: 900;
    color: #FEF3C7;
    max-width: 195px;
    line-height: 1.25;
  }

  .draggable-bookmarks {
    flex: 1;
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: 10px;
  }

  .seq-bookmark-card {
    height: 54px;
    border-radius: 14px;
    border: 3.5px solid #FFFBEB;
    font-family: inherit;
    cursor: grab;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    color: #FFFFFF;
    box-shadow: 0 5px 0 rgba(0,0,0,0.35);
    transition: transform 0.14s cubic-bezier(0.34, 1.56, 0.64, 1), opacity 0.15s, box-shadow 0.14s;
    position: relative;
  }
  .seq-bookmark-card:hover:not(.placed) {
    transform: translateY(-3px) scale(1.03);
  }
  .seq-bookmark-card.selected {
    box-shadow: 0 0 0 4px #FDE047, 0 6px 0 rgba(0,0,0,0.4);
    transform: translateY(-3px) scale(1.05);
  }
  .seq-bookmark-card.placed {
    opacity: 0.28;
    cursor: default;
    transform: scale(0.94);
    box-shadow: none;
  }
  .seq-bookmark-card.shake {
    animation: shakeErr 0.4s ease;
  }

  @keyframes shakeErr {
    0%, 100% { transform: translateX(0); }
    20% { transform: translateX(-8px); }
    40% { transform: translateX(8px); }
    60% { transform: translateX(-5px); }
    80% { transform: translateX(5px); }
  }

  .seq-bookmark-card.first { background: linear-gradient(180deg, #F43F5E, #BE123C); }
  .seq-bookmark-card.next  { background: linear-gradient(180deg, #F59E0B, #B45309); }
  .seq-bookmark-card.then  { background: linear-gradient(180deg, #3B82F6, #1D4ED8); }
  .seq-bookmark-card.last  { background: linear-gradient(180deg, #10B981, #047857); }

  .sbc-word {
    font-size: 20px;
    font-weight: 900;
    letter-spacing: 0.8px;
    line-height: 1;
  }
  .sbc-sub {
    font-size: 10px;
    font-weight: 800;
    opacity: 0.92;
    margin-top: 2px;
  }

  /* 4 STORYBOOK PAGE SLOTS GRID */
  .four-pages-grid {
    flex: 1;
    min-height: 0;
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: 10px;
  }

  .story-page-slot {
    background: #FFFFFF;
    border: 3.5px dashed #CBD5E1;
    border-radius: 20px;
    padding: 8px 9px;
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    position: relative;
    transition: all 0.2s ease;
    cursor: pointer;
    overflow: hidden;
  }

  .story-page-slot:hover {
    border-color: #F59E0B;
    background: #FFFDF7;
  }

  .story-page-slot.filled {
    border-style: solid;
    background: linear-gradient(180deg, #FFFFFF 0%, #FFFBEB 100%);
    box-shadow: 0 8px 18px rgba(120, 53, 15, 0.14);
  }
  .story-page-slot.filled.first { border-color: var(--c-first); }
  .story-page-slot.filled.next  { border-color: var(--c-next); }
  .story-page-slot.filled.then  { border-color: var(--c-then); }
  .story-page-slot.filled.last  { border-color: var(--c-last); }

  .sps-step-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    font-size: 11.5px;
    font-weight: 900;
    color: #64748B;
  }

  /* Target Drop Slot for Sequence Word Bookmark */
  .sps-word-dropzone {
    height: 44px;
    border-radius: 12px;
    border: 2.5px dashed #94A3B8;
    background: #F1F5F9;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 13px;
    font-weight: 900;
    color: #64748B;
    margin: 4px 0;
    transition: all 0.2s;
  }

  .sps-word-dropzone.locked {
    border-style: solid;
    border-color: #FFFFFF;
    color: #FFFFFF;
    font-size: 19px;
    letter-spacing: 0.8px;
    box-shadow: 0 4px 0 rgba(0,0,0,0.2);
  }
  .sps-word-dropzone.locked.first { background: linear-gradient(180deg, #F43F5E, #BE123C); }
  .sps-word-dropzone.locked.next  { background: linear-gradient(180deg, #F59E0B, #B45309); }
  .sps-word-dropzone.locked.then  { background: linear-gradient(180deg, #3B82F6, #1D4ED8); }
  .sps-word-dropzone.locked.last  { background: linear-gradient(180deg, #10B981, #047857); }

  /* Illustrated Diorama Plaque inside each Story Page */
  .sps-illustration-box {
    height: 112px;
    border-radius: 14px;
    border: 2.5px solid #E2E8F0;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    position: relative;
    overflow: hidden;
    background: linear-gradient(180deg, #E0F2FE 0%, #FEF9C3 100%);
  }

  .sps-emoji-stage {
    font-size: 46px;
    line-height: 1;
    filter: drop-shadow(0 4px 6px rgba(0,0,0,0.16));
    z-index: 2;
  }

  .sps-scene-caption {
    font-size: 10.5px;
    font-weight: 900;
    background: rgba(255,255,255,0.86);
    padding: 2px 8px;
    border-radius: 999px;
    color: #334155;
    margin-top: 4px;
    z-index: 2;
  }

  .sps-story-text {
    font-size: 12.5px;
    font-weight: 800;
    color: #1E293B;
    line-height: 1.32;
    text-align: left;
    flex: 1;
    display: flex;
    align-items: center;
    padding: 4px 2px;
  }

  .sps-story-text b.seq-lead {
    padding: 1px 6px;
    border-radius: 6px;
    color: #fff;
    margin-right: 4px;
  }
  .sps-story-text b.seq-lead.first { background: var(--c-first); }
  .sps-story-text b.seq-lead.next  { background: var(--c-next); }
  .sps-story-text b.seq-lead.then  { background: var(--c-then); }
  .sps-story-text b.seq-lead.last  { background: var(--c-last); }

  .sps-footer-hint {
    font-size: 10.5px;
    font-weight: 900;
    text-align: center;
    padding: 4px 6px;
    border-radius: 8px;
    background: #FEF3C7;
    color: #92400E;
    border: 1.5px solid #FCD34D;
  }

  /* ============ MODE 3: SEQUENCE WORD DETECTIVE ============ */
  .detective-card {
    flex: 1;
    background: #FFFFFF;
    border: 4px solid #F59E0B;
    border-radius: 22px;
    padding: 14px 20px;
    margin: 6px 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: space-between;
    box-shadow: 0 8px 24px rgba(120, 53, 15, 0.14);
  }

  .timeline-stepper {
    display: flex;
    align-items: center;
    gap: 10px;
  }
  .tl-dot {
    padding: 5px 14px;
    border-radius: 999px;
    font-size: 13px;
    font-weight: 900;
    background: #F1F5F9;
    color: #64748B;
    border: 2px solid #CBD5E1;
  }
  .tl-dot.current {
    background: #FEF08A;
    color: #78350F;
    border-color: #D97706;
    transform: scale(1.08);
    box-shadow: 0 4px 0 #B45309;
  }

  /* ============ 4. RIGHT SIDE: 3-TEAM BOOKWORM STACK RACE ============ */
  .bookworm-race {
    background: linear-gradient(180deg, #78350F 0%, #451A03 100%);
    border-radius: 24px;
    border: 5px solid #D97706;
    box-shadow: 0 12px 28px rgba(0, 0, 0, 0.4);
    padding: 10px 11px;
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    color: #FFFBEB;
    position: relative;
    overflow: hidden;
  }

  .br-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    background: rgba(255, 255, 255, 0.12);
    padding: 6px 10px;
    border-radius: 14px;
    border: 2px solid rgba(253, 224, 71, 0.35);
  }

  .br-title {
    font-size: 14px;
    font-weight: 900;
    color: #FDE047;
    display: flex;
    align-items: center;
    gap: 5px;
  }

  .book-stacks-grid {
    flex: 1;
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 7px;
    padding: 8px 0 4px;
    min-height: 0;
  }

  .team-pedestal-col {
    background: rgba(255, 255, 255, 0.09);
    border: 2px solid rgba(255, 255, 255, 0.22);
    border-radius: 15px;
    padding: 5px 4px;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: space-between;
    cursor: pointer;
    transition: all 0.15s;
  }
  .team-pedestal-col.active-turn {
    background: rgba(254, 243, 199, 0.22);
    border-color: #FDE047;
    box-shadow: 0 0 0 3px rgba(253, 224, 71, 0.35);
  }

  .tp-badge {
    width: 100%;
    background: #FFFBEB;
    color: #1E293B;
    border-radius: 10px;
    padding: 3px 4px;
    text-align: center;
    border: 2px solid;
  }

  .stack-column {
    flex: 1;
    width: 100%;
    display: flex;
    flex-direction: column-reverse;
    align-items: center;
    justify-content: flex-start;
    padding: 4px 0;
    overflow: hidden;
  }

  .oak-shelf-base {
    width: 88%;
    height: 9px;
    background: linear-gradient(90deg, #B45309, #F59E0B, #B45309);
    border: 2px solid #451A03;
    border-radius: 4px;
    flex-shrink: 0;
  }

  .stacked-book {
    width: 78%;
    height: 18px;
    border-radius: 4px 7px 7px 4px;
    border: 2px solid #FFFBEB;
    margin-bottom: 1px;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 10px;
    font-weight: 900;
    color: #fff;
    box-shadow: 0 2px 4px rgba(0,0,0,0.3);
    animation: dropBook 0.25s cubic-bezier(0.34, 1.56, 0.64, 1);
  }
  @keyframes dropBook {
    0% { transform: translateY(-20px); opacity: 0; }
    100% { transform: translateY(0); opacity: 1; }
  }

  /* ============ 5. ENCHANTED POP-UP STORYBOOK DETAIL VIEWER MODAL ============ */
  .modal-overlay {
    position: absolute;
    inset: 0;
    background: rgba(28, 13, 4, 0.82);
    backdrop-filter: blur(6px);
    z-index: 80;
    display: none;
    align-items: center;
    justify-content: center;
    padding: 20px;
  }
  .modal-overlay.open { display: flex; }

  .popup-storybook {
    width: 920px;
    height: 510px;
    background: linear-gradient(90deg, #FEF9E7 0%, #FFFDF7 48%, #E2D5B5 50%, #FFFDF7 52%, #FEF9E7 100%);
    border: 8px solid #92400E;
    border-radius: 28px;
    box-shadow: 0 28px 70px rgba(0,0,0,0.65), inset 0 0 0 4px #F59E0B;
    display: grid;
    grid-template-columns: 1fr 1fr;
    padding: 24px 28px;
    gap: 28px;
    position: relative;
  }

  .ps-left-page {
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    padding-right: 8px;
  }

  .ps-right-page {
    background: #FFFFFF;
    border: 5px solid #F59E0B;
    border-radius: 22px;
    overflow: hidden;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    position: relative;
    box-shadow: inset 0 4px 16px rgba(0,0,0,0.08);
  }

  /* Confetti Layer */
  .confetti-layer {
    position: absolute;
    inset: 0;
    pointer-events: none;
    z-index: 90;
    overflow: hidden;
  }
  .confetti-dot {
    position: absolute;
    width: 12px;
    height: 12px;
    border-radius: 50%;
    animation: burstOut 0.9s cubic-bezier(0.16, 1, 0.3, 1) forwards;
  }
  @keyframes burstOut {
    0%   { transform: translate(0, 0) scale(1); opacity: 1; }
    100% { transform: translate(var(--tx), var(--ty)) scale(0.1); opacity: 0; }
  }
</style>
</head>
<body>
<div class="viewport-shell">
  <div class="stage-1280" id="appStage">
    <div class="library-backdrop"></div>

    <!-- 1. TOP HEADER BAR -->
    <header class="top-bar">
      <div class="brand-cluster">
        <div class="brand-logo">📚</div>
        <div>
          <div class="brand-title">Storybook Sequence Library</div>
          <div class="brand-sub">Tell the Steps of Every Story in Order: FIRST &bull; NEXT &bull; THEN &bull; LAST</div>
        </div>
      </div>

      <nav class="mode-tabs" aria-label="Library Game Modes">
        <button class="mode-tab active" data-mode="words" type="button">
          <span>🔖</span> <span>1. Place Sequence Words</span>
        </button>
        <button class="mode-tab" data-mode="cards" type="button">
          <span>🔀</span> <span>2. Order Story Pages</span>
        </button>
        <button class="mode-tab" data-mode="quiz" type="button">
          <span>🕵️</span> <span>3. Sequence Detective</span>
        </button>
      </nav>

      <div class="top-right">
        <div class="star-pill"><span>⭐</span> <span id="starCount">0</span></div>
        <button class="util-btn" id="repeatBtn" type="button"><span>🔊</span> Repeat</button>
        <button class="util-btn" id="resetBtn" type="button"><span>🔄</span> Reset</button>
        <button class="util-btn" id="soundBtn" type="button"><span id="soundIcon">🔊</span> Sound</button>
        <button class="util-btn" id="fullBtn" type="button"><span>⛶</span> Full</button>
      </div>
    </header>

    <!-- 2. SEQUENCE WORD RULES & 10-BOOK LIBRARY SHELF -->
    <section class="library-shelf-bar" aria-label="Sequence Words and Bookshelf">
      <div class="seq-rule-pills">
        <button class="seq-rule-btn first" data-rule="first" type="button" title="Tap to hear how FIRST is used!">
          <span class="srb-tag">1. FIRST</span>
          <span class="srb-lbl">Beginning<small>Step 1</small></span>
        </button>
        <button class="seq-rule-btn next" data-rule="next" type="button" title="Tap to hear how NEXT is used!">
          <span class="srb-tag">2. NEXT</span>
          <span class="srb-lbl">After That<small>Step 2</small></span>
        </button>
        <button class="seq-rule-btn then" data-rule="then" type="button" title="Tap to hear how THEN is used!">
          <span class="srb-tag">3. THEN</span>
          <span class="srb-lbl">Keep Going<small>Step 3</small></span>
        </button>
        <button class="seq-rule-btn last" data-rule="last" type="button" title="Tap to hear how LAST is used!">
          <span class="srb-tag">4. LAST</span>
          <span class="srb-lbl">The Ending<small>Step 4</small></span>
        </button>
      </div>

      <div class="book-spines-row" id="bookSpinesRow"></div>
    </section>

    <!-- 3. MAIN SPLIT: LEFT READING DESK + RIGHT 3-TEAM BOOKWORM RACE -->
    <main class="main-split">
      <section class="reading-desk" id="readingDesk" aria-label="Interactive Storybook Desk"></section>

      <aside class="bookworm-race" aria-label="3-Team Library Book Stack Race">
        <div class="br-head">
          <div class="br-title"><span>🦉</span> <span>Library Book Stack</span></div>
          <button class="lib-btn" id="whoWinsBtn" type="button" style="padding:3px 9px;font-size:11.5px">🏆 Winner</button>
        </div>

        <div class="book-stacks-grid" id="bookStacksGrid"></div>

        <div style="font-size:11px;font-weight:800;color:#FDE68A;text-align:center;background:rgba(0,0,0,0.28);padding:5px;border-radius:10px;border:1.5px solid rgba(253,224,71,0.3)" id="raceStatusTxt">
          ✨ Placing sequence words stacks a hardcover book for the active team!
        </div>
      </aside>
    </main>

    <!-- 4. FULL-SCREEN POP-UP STORYBOOK DETAIL VIEWER (Upgraded from original DETAIL state!) -->
    <div class="modal-overlay" id="detailModal" role="dialog" aria-modal="true">
      <div class="popup-storybook">
        <div class="ps-left-page">
          <div>
            <div style="display:flex;align-items:center;justify-content:space-between">
              <span id="detailStepBadge" style="padding:6px 18px;border-radius:12px;font-size:26px;font-weight:900;color:#fff;background:#E11D48">1. FIRST</span>
              <span id="detailBookTitle" style="font-size:14px;font-weight:900;color:#92400E">🎮 Book 1: Ben's Game</span>
            </div>
            <p id="detailStoryText" style="font-size:24px;font-weight:900;color:#1E293B;line-height:1.45;margin:22px 0"></p>
          </div>

          <div style="display:flex;flex-wrap:wrap;gap:10px">
            <button class="lib-btn green" id="detailReadBtn" type="button" style="font-size:15px;padding:10px 18px">
              <span>🇬🇧</span> <span>Read This Page Aloud</span>
            </button>
            <button class="lib-btn purple" id="detailReadAllBtn" type="button" style="font-size:15px;padding:10px 18px">
              <span>📖</span> <span>Read Whole Story</span>
            </button>
            <button class="lib-btn" id="detailCloseBtn" type="button" style="font-size:15px;padding:10px 18px">
              <span>🔙</span> <span>Back to Desk</span>
            </button>
          </div>
        </div>

        <div class="ps-right-page" id="detailRightIllustration"></div>
      </div>
    </div>

    <!-- 5. WINNER MODAL -->
    <div class="modal-overlay" id="winnerModal" role="dialog" aria-modal="true">
      <div style="background:linear-gradient(180deg,#FFFBEB,#FEF3C7);border:6px solid #D97706;border-radius:28px;padding:28px 36px;text-align:center;max-width:500px;color:#1E293B;box-shadow:0 25px 60px rgba(0,0,0,0.6)">
        <div style="font-size:68px;line-height:1">👑📚</div>
        <h2 style="font-size:32px;font-weight:900;color:#92400E;margin:8px 0">Master Storytellers!</h2>
        <div style="font-size:24px;font-weight:900;color:#1E3A8A;margin-bottom:18px" id="winnerText">Team Owl Wins!</div>
        <div style="display:flex;justify-content:center;gap:12px">
          <button class="lib-btn" id="closeWinnerBtn" type="button" style="font-size:15px;padding:8px 18px">Keep Reading</button>
          <button class="lib-btn green" id="newRaceBtn" type="button" style="font-size:15px;padding:8px 18px">🔄 New Book Stack</button>
        </div>
      </div>
    </div>

    <!-- CONFETTI LAYER -->
    <div class="confetti-layer" id="confettiLayer"></div>
  </div>
</div>

<script src="assets/audio_sequence_library.js"></script>
<script>
(function() {
  "use strict";

  /* ============ 1. PROPORTIONAL 1280x720 CONTAINMENT ============ */
  var BASE_W = 1280;
  var BASE_H = 720;
  function fitAppToScreen() {
    var vw = window.innerWidth || document.documentElement.clientWidth || BASE_W;
    var vh = window.innerHeight || document.documentElement.clientHeight || BASE_H;
    var pad = (vw < 900 || vh < 600) ? 4 : 10;
    var scale = Math.min((vw - pad * 2) / BASE_W, (vh - pad * 2) / BASE_H);
    scale = Math.max(0.28, Math.min(scale, 1.35));
    document.documentElement.style.setProperty("--app-scale", scale.toFixed(4));
  }
  window.addEventListener("resize", fitAppToScreen);
  window.addEventListener("orientationchange", fitAppToScreen);
  fitAppToScreen();

  /* ============ 2. 10 COMPLETE 4-STEP STORYBOOKS (40 CARDS!) ============ */
  var SEQ_META = [
    { id: "first", num: 1, label: "FIRST", sub: "1st • Beginning", color: "#E11D48" },
    { id: "next",  num: 2, label: "NEXT",  sub: "2nd • Step Two",  color: "#D97706" },
    { id: "then",  num: 3, label: "THEN",  sub: "3rd • Step Three",color: "#2563EB" },
    { id: "last",  num: 4, label: "LAST",  sub: "4th • The Ending",color: "#059669" }
  ];

  var STORYBOOKS = [
    {
      id: "b01",
      shortTitle: "🎮 1. Ben's Game",
      fullTitle: "Book 1: Ben's Video Game Award (“Stop Light”)",
      icon: "🎮",
      steps: [
        { id: "first", label: "FIRST", emoji: "🏆🎮", sceneTag: "Winning the Award", bg: "linear-gradient(135deg,#FEF3C7,#FDE68A)", imgUrl: "https://i.ibb.co/d4vwNpP1/image.png", text: "Ben wins an award for his video game, “Stop Light”, and is asked to give a speech." },
        { id: "next",  label: "NEXT",  emoji: "😰🎤", sceneTag: "Called to the Stage", bg: "linear-gradient(135deg,#DBEAFE,#BFDBFE)", imgUrl: "https://i.ibb.co/gLwrrY1M/image.png", text: "Ben is nervous, but still attends the ceremony. He is called to the stage as the winner." },
        { id: "then",  label: "THEN",  emoji: "🚦💡", sceneTag: "Red Light, Green Light", bg: "linear-gradient(135deg,#DCFCE7,#A7F3D0)", imgUrl: "https://i.ibb.co/tPkfrnRr/image.png", text: "Ben talks about playing “Red Light, Green Light” which made him make the game." },
        { id: "last",  label: "LAST",  emoji: "👏😊", sceneTag: "Everyone Claps!", bg: "linear-gradient(135deg,#FCE7F3,#FBCFE8)", imgUrl: "https://i.ibb.co/ZpC5dFZ3/image.png", text: "Ben finishes his speech and everyone claps. He feels proud of his work." }
      ]
    },
    {
      id: "b02",
      shortTitle: "🌻 2. Sunflower",
      fullTitle: "Book 2: The Little Sunflower Seed",
      icon: "🌻",
      steps: [
        { id: "first", label: "FIRST", emoji: "🌱🕳️", sceneTag: "Planting the Seed", bg: "linear-gradient(135deg,#FEF3C7,#FDE68A)", text: "Maya digs a small hole in the warm garden soil and drops in a tiny sunflower seed." },
        { id: "next",  label: "NEXT",  emoji: "🚿💧", sceneTag: "Watering Every Day", bg: "linear-gradient(135deg,#E0F2FE,#BAE6FD)", text: "She waters the soil every morning with her shiny blue watering can." },
        { id: "then",  label: "THEN",  emoji: "🌿☀️", sceneTag: "Sprouting Leaves", bg: "linear-gradient(135deg,#DCFCE7,#86EFAC)", text: "A green stem and two little leaves pop up toward the bright sun." },
        { id: "last",  label: "LAST",  emoji: "🌻🐝", sceneTag: "Giant Golden Flower", bg: "linear-gradient(135deg,#FEF08A,#FACC15)", text: "A giant golden sunflower blooms taller than Maya's head!" }
      ]
    },
    {
      id: "b03",
      shortTitle: "🥞 3. Pancakes",
      fullTitle: "Book 3: Sunday Morning Pancakes",
      icon: "🥞",
      steps: [
        { id: "first", label: "FIRST", emoji: "🥣🥚", sceneTag: "Mixing the Batter", bg: "linear-gradient(135deg,#FEF3C7,#FDE68A)", text: "Leo mixes flour, fresh milk, and one egg in a big yellow bowl." },
        { id: "next",  label: "NEXT",  emoji: "🍳🔥", sceneTag: "Pouring on the Pan", bg: "linear-gradient(135deg,#FFEDD5,#FDBA74)", text: "Dad pours a round circle of batter onto the warm frying pan." },
        { id: "then",  label: "THEN",  emoji: "🫧🤸", sceneTag: "Flipping the Pancake", bg: "linear-gradient(135deg,#DBEAFE,#93C5FD)", text: "Little bubbles pop on top, and Leo flips the golden pancake in the air!" },
        { id: "last",  label: "LAST",  emoji: "🥞🍯", sceneTag: "Syrup & Breakfast!", bg: "linear-gradient(135deg,#DCFCE7,#86EFAC)", text: "They stack the warm pancakes on a plate with sweet maple syrup." }
      ]
    },
    {
      id: "b04",
      shortTitle: "🐣 4. Robin's Egg",
      fullTitle: "Book 4: The Mystery Blue Egg",
      icon: "🐣",
      steps: [
        { id: "first", label: "FIRST", emoji: "🪺🌳", sceneTag: "Building the Nest", bg: "linear-gradient(135deg,#DCFCE7,#BBF7D0)", text: "Mother Robin builds a cozy twig nest high up in the green apple tree." },
        { id: "next",  label: "NEXT",  emoji: "🥚🐦", sceneTag: "Keeping Eggs Warm", bg: "linear-gradient(135deg,#E0F2FE,#BAE6FD)", text: "She sits on three shiny blue eggs to keep them warm and safe." },
        { id: "then",  label: "THEN",  emoji: "🐣💥", sceneTag: "Tap, Tap, Hatch!", bg: "linear-gradient(135deg,#FEF9C3,#FDE047)", text: "A tiny crack appears—tap, tap, tap—and a baby bird pokes its beak out!" },
        { id: "last",  label: "LAST",  emoji: "🐦☁️", sceneTag: "Flying in the Sky", bg: "linear-gradient(135deg,#DBEAFE,#60A5FA)", text: "The little robin grows soft feathers and flies high into the blue sky!" }
      ]
    },
    {
      id: "b05",
      shortTitle: "⛄ 5. Snowman",
      fullTitle: "Book 5: Building Frosty the Snowman",
      icon: "⛄",
      steps: [
        { id: "first", label: "FIRST", emoji: "❄️🌨️", sceneTag: "Snowy Blanket", bg: "linear-gradient(135deg,#E0F2FE,#BAE6FD)", text: "Fluffy white snowflakes fall all night and cover the yard like a blanket." },
        { id: "next",  label: "NEXT",  emoji: "⚪⛄", sceneTag: "Rolling 3 Snowballs", bg: "linear-gradient(135deg,#DBEAFE,#93C5FD)", text: "Sam and Lily roll three big snowballs and stack them from biggest to smallest." },
        { id: "then",  label: "THEN",  emoji: "🥕👀", sceneTag: "Carrot Nose & Eyes", bg: "linear-gradient(135deg,#FFEDD5,#FDBA74)", text: "They push in a crunchy orange carrot nose, button eyes, and two stick arms." },
        { id: "last",  label: "LAST",  emoji: "🧣🎩", sceneTag: "Scarf & Top Hat!", bg: "linear-gradient(135deg,#FCE7F3,#F9A8D4)", text: "They wrap a cozy red scarf around the snowman and put a top hat on its head!" }
      ]
    },
    {
      id: "b06",
      shortTitle: "🦷 6. Bedtime",
      fullTitle: "Book 6: Toby's Cozy Bedtime Routine",
      icon: "😴",
      steps: [
        { id: "first", label: "FIRST", emoji: "🛁🫧", sceneTag: "Warm Bubble Bath", bg: "linear-gradient(135deg,#E0F2FE,#BAE6FD)", text: "Toby takes a warm bubble bath and puts on his blue star pajamas." },
        { id: "next",  label: "NEXT",  emoji: "🪥🦷", sceneTag: "Brushing Teeth", bg: "linear-gradient(135deg,#DCFCE7,#A7F3D0)", text: "He squeezes minty toothpaste onto his brush and brushes his teeth." },
        { id: "then",  label: "THEN",  emoji: "📖🐉", sceneTag: "Bedtime Story", bg: "linear-gradient(135deg,#FEF3C7,#FDE68A)", text: "Mom sits by his bed and reads a library book about friendly dragons." },
        { id: "last",  label: "LAST",  emoji: "🧸😴", sceneTag: "Fast Asleep", bg: "linear-gradient(135deg,#EDE9FE,#C4B5FD)", text: "Toby hugs his soft teddy bear, closes his eyes, and falls fast asleep." }
      ]
    },
    {
      id: "b07",
      shortTitle: "🦋 7. Butterfly",
      fullTitle: "Book 7: The Hungry Caterpillar's Surprise",
      icon: "🦋",
      steps: [
        { id: "first", label: "FIRST", emoji: "🐛🍃", sceneTag: "Tiny Caterpillar", bg: "linear-gradient(135deg,#DCFCE7,#BBF7D0)", text: "A tiny green caterpillar hatches on a sunny leaf in the garden." },
        { id: "next",  label: "NEXT",  emoji: "🥬😋", sceneTag: "Munching Leaves", bg: "linear-gradient(135deg,#FEF9C3,#FDE047)", text: "The hungry caterpillar munches crunchy green leaves all week to grow big." },
        { id: "then",  label: "THEN",  emoji: "🥜🌿", sceneTag: "Cozy Chrysalis", bg: "linear-gradient(135deg,#E0F2FE,#BAE6FD)", text: "It spins a quiet green chrysalis shell around itself and rests inside." },
        { id: "last",  label: "LAST",  emoji: "🦋🌸", sceneTag: "Beautiful Butterfly!", bg: "linear-gradient(135deg,#FCE7F3,#F9A8D4)", text: "The shell opens and a colorful orange butterfly spreads its wings and flies!" }
      ]
    },
    {
      id: "b08",
      shortTitle: "🍕 8. Pizza",
      fullTitle: "Book 8: Chef Mia's Homemade Pizza",
      icon: "🍕",
      steps: [
        { id: "first", label: "FIRST", emoji: "🫓👐", sceneTag: "Rolling the Dough", bg: "linear-gradient(135deg,#FEF3C7,#FDE68A)", text: "Chef Mia rolls the soft pizza dough into a big round circle." },
        { id: "next",  label: "NEXT",  emoji: "🍅🥄", sceneTag: "Spreading Tomato Sauce", bg: "linear-gradient(135deg,#FEE2E2,#FCA5A5)", text: "She spreads bright red tomato sauce all over the dough with a spoon." },
        { id: "then",  label: "THEN",  emoji: "🧀🫑", sceneTag: "Cheese & Peppers", bg: "linear-gradient(135deg,#FEF9C3,#FDE047)", text: "She sprinkles stretchy white cheese and sweet bell peppers on top." },
        { id: "last",  label: "LAST",  emoji: "🍕♨️", sceneTag: "Hot & Ready to Share!", bg: "linear-gradient(135deg,#FFEDD5,#FDBA74)", text: "The pizza bakes in the oven until the crust is golden and ready to share!" }
      ]
    },
    {
      id: "b09",
      shortTitle: "🏖️ 9. Sandcastle",
      fullTitle: "Book 9: Noah's Royal Sandcastle",
      icon: "🏰",
      steps: [
        { id: "first", label: "FIRST", emoji: "🪣🌊", sceneTag: "Filling the Bucket", bg: "linear-gradient(135deg,#E0F2FE,#BAE6FD)", text: "Noah fills his red bucket with wet sand near the splashing ocean waves." },
        { id: "next",  label: "NEXT",  emoji: "🏰✨", sceneTag: "Shaping 4 Towers", bg: "linear-gradient(135deg,#FEF3C7,#FDE68A)", text: "He flips the bucket upside down and lifts it to make four tall sand towers." },
        { id: "then",  label: "THEN",  emoji: "🐚🦀", sceneTag: "Seashell Walls", bg: "linear-gradient(135deg,#FCE7F3,#FBCFE8)", text: "He decorates the castle walls with shiny seashells and digs a moat." },
        { id: "last",  label: "LAST",  emoji: "🚩📸", sceneTag: "Blue Flag on Top!", bg: "linear-gradient(135deg,#DCFCE7,#86EFAC)", text: "He places a little blue flag on the highest tower and smiles for a photo!" }
      ]
    },
    {
      id: "b10",
      shortTitle: "🚀 10. Moon Rocket",
      fullTitle: "Book 10: Blast Off to the Moon!",
      icon: "🚀",
      steps: [
        { id: "first", label: "FIRST", emoji: "🧑‍🚀🥽", sceneTag: "Putting on Spacesuit", bg: "linear-gradient(135deg,#E0F2FE,#BAE6FD)", text: "Astronaut Ava puts on her white spacesuit, boots, and shiny helmet." },
        { id: "next",  label: "NEXT",  emoji: "🔟🎙️", sceneTag: "3, 2, 1 Countdown!", bg: "linear-gradient(135deg,#FEF3C7,#FDE68A)", text: "She climbs into the tall rocket ship and counts down: Three, Two, One!" },
        { id: "then",  label: "THEN",  emoji: "🚀🔥", sceneTag: "Blast Off into Space!", bg: "linear-gradient(135deg,#FFEDD5,#FDBA74)", text: "The engines roar with bright flames and the rocket zooms up into space!" },
        { id: "last",  label: "LAST",  emoji: "🌕🌎", sceneTag: "Waving from the Moon!", bg: "linear-gradient(135deg,#EDE9FE,#C4B5FD)", text: "Ava lands gently on the Moon and waves back at planet Earth!" }
      ]
    }
  ];

  var TEAMS = [
    { name: "Team Owl",  icon: "🦉", c: "#D97706", bookColors: ["#E11D48", "#F59E0B", "#2563EB", "#059669"] },
    { name: "Team Fox",  icon: "🦊", c: "#EA580C", bookColors: ["#2563EB", "#8B5CF6", "#EC4899", "#F59E0B"] },
    { name: "Team Lion", icon: "🦁", c: "#059669", bookColors: ["#059669", "#D97706", "#E11D48", "#3B82F6"] }
  ];

  function shuffle(arr) {
    var a = arr.slice();
    for (var i = a.length - 1; i > 0; i--) {
      var j = Math.floor(Math.random() * (i + 1));
      var tmp = a[i]; a[i] = a[j]; a[j] = tmp;
    }
    return a;
  }

  /* ============ 3. STATE ============ */
  var state = {
    mode: "words",            // "words" | "cards" | "quiz"
    bookIdx: 0,
    filledSlots: [false, false, false, false],
    trayOrder: [2, 0, 3, 1],  // shuffled indices 0..3
    selectedTrayIdx: null,
    quizStepIdx: 0,
    quizSolved: false,
    stars: 0,
    activeTeam: 0,
    teamScores: [0, 0, 0],
    detailStepIdx: 0,
    soundOn: true,
    audioCtx: null,
    decoded: {},
    activeSources: [],
    activeHtmlAudio: null,
    preloadedImages: {}
  };

  /* Preload original Book 1 images cleanly */
  STORYBOOKS[0].steps.forEach(function(st) {
    if (st.imgUrl) {
      var img = new Image();
      img.crossOrigin = "Anonymous";
      img.src = st.imgUrl;
      state.preloadedImages[st.id] = img;
    }
  });

  function currentBook() {
    return STORYBOOKS[state.bookIdx % STORYBOOKS.length];
  }

  function resetCurrentBookPuzzle() {
    state.filledSlots = [false, false, false, false];
    state.trayOrder = shuffle([0, 1, 2, 3]);
    // Ensure it isn't trivially [0,1,2,3]
    if (state.trayOrder[0] === 0 && state.trayOrder[1] === 1 && state.trayOrder[2] === 2) {
      state.trayOrder = [2, 0, 3, 1];
    }
    state.selectedTrayIdx = null;
    state.quizSolved = false;
  }

  /* ============ 4. STUDIO VOICE ENGINE (JENNY US FEMALE + RYAN UK MALE) ============ */
  function ensureAudio() {
    if (!state.audioCtx) {
      var AC = window.AudioContext || window.webkitAudioContext;
      if (AC) state.audioCtx = new AC();
    }
    if (state.audioCtx && state.audioCtx.state === "suspended") {
      state.audioCtx.resume().catch(function() {});
    }
    return state.audioCtx;
  }

  function stopAllSpeech() {
    state.activeSources.forEach(function(s) {
      try { s.stop(0); } catch (e) {}
    });
    state.activeSources = [];
    if (state.activeHtmlAudio) {
      try {
        state.activeHtmlAudio.pause();
        state.activeHtmlAudio.currentTime = 0;
      } catch (e) {}
      state.activeHtmlAudio = null;
    }
  }

  function playTone(freq, dur, type) {
    if (!state.soundOn) return;
    var ctx = ensureAudio();
    if (!ctx) return;
    try {
      var o = ctx.createOscillator();
      var g = ctx.createGain();
      var t = ctx.currentTime;
      o.type = type || "sine";
      o.frequency.setValueAtTime(freq, t);
      o.frequency.exponentialRampToValueAtTime(freq * 1.3, t + (dur || 0.14));
      g.gain.setValueAtTime(0.0001, t);
      g.gain.exponentialRampToValueAtTime(0.09, t + 0.015);
      g.gain.exponentialRampToValueAtTime(0.0001, t + (dur || 0.14));
      o.connect(g); g.connect(ctx.destination);
      o.start(t); o.stop(t + (dur || 0.14) + 0.02);
    } catch (e) {}
  }

  function computeTrimSpan(buf) {
    var ch = buf.getChannelData(0);
    var sr = buf.sampleRate;
    var n = ch.length;
    var thr = 0.012;
    var first = 0;
    var last = n - 1;
    for (var i = 0; i < n; i++) {
      if (Math.abs(ch[i]) > thr) { first = i; break; }
    }
    for (var j = n - 1; j >= 0; j--) {
      if (Math.abs(ch[j]) > thr) { last = j; break; }
    }
    var start = Math.max(0, (first / sr) - 0.02);
    var end = Math.min(buf.duration, (last / sr) + 0.045);
    return [start, Math.max(start + 0.08, end)];
  }

  async function decodeClip(key) {
    if (state.decoded[key]) return state.decoded[key];
    var meta = window.SEQUENCE_LIBRARY_AUDIO && window.SEQUENCE_LIBRARY_AUDIO[key];
    if (!meta || !meta.src) return null;
    var ctx = ensureAudio();
    if (!ctx) return null;
    var b64 = meta.src.split(",")[1];
    var bin = atob(b64);
    var bytes = new Uint8Array(bin.length);
    for (var i = 0; i < bin.length; i++) bytes[i] = bin.charCodeAt(i);
    var buf = await ctx.decodeAudioData(bytes.buffer.slice(0));
    buf._trimSpan = computeTrimSpan(buf);
    state.decoded[key] = buf;
    return buf;
  }

  async function playClips(keys) {
    if (!state.soundOn) return;
    stopAllSpeech();
    var valid = (keys || []).filter(function(k) {
      return window.SEQUENCE_LIBRARY_AUDIO && window.SEQUENCE_LIBRARY_AUDIO[k];
    });
    if (!valid.length) return;

    var ctx = ensureAudio();
    if (ctx) {
      try {
        var bufs = await Promise.all(valid.map(decodeClip));
        var cursor = ctx.currentTime + 0.02;
        bufs.forEach(function(buf) {
          if (!buf) return;
          var span = buf._trimSpan || [0, buf.duration];
          var start = span[0];
          var dur = Math.max(0.08, span[1] - start);
          var src = ctx.createBufferSource();
          src.buffer = buf;
          src.connect(ctx.destination);
          src.start(cursor, start, dur);
          state.activeSources.push(src);
          cursor += dur + 0.09;
        });
        return;
      } catch (e) {}
    }

    for (var i = 0; i < valid.length; i++) {
      var item = window.SEQUENCE_LIBRARY_AUDIO[valid[i]];
      if (!item) continue;
      await new Promise(function(resolve) {
        var a = new Audio(item.src);
        state.activeHtmlAudio = a;
        a.onended = resolve;
        a.onerror = resolve;
        a.play().catch(resolve);
      });
    }
  }

  function playClip(key) {
    return playClips([key]);
  }

  function triggerConfetti() {
    var layer = document.getElementById("confettiLayer");
    if (!layer) return;
    var colors = ["#E11D48", "#D97706", "#2563EB", "#059669", "#FACC15", "#A855F7"];
    for (var i = 0; i < 34; i++) {
      var dot = document.createElement("div");
      dot.className = "confetti-dot";
      var angle = (Math.PI * 2 * i) / 34 + (Math.random() * 0.25);
      var dist = 90 + Math.random() * 230;
      dot.style.left = "46%";
      dot.style.top = "48%";
      dot.style.background = colors[i % colors.length];
      dot.style.setProperty("--tx", Math.cos(angle) * dist + "px");
      dot.style.setProperty("--ty", Math.sin(angle) * dist + "px");
      layer.appendChild(dot);
      setTimeout(function(el) {
        if (el && el.parentNode) el.parentNode.removeChild(el);
      }, 950, dot);
    }
  }

  function awardTeamBook() {
    state.stars += 1;
    document.getElementById("starCount").textContent = String(state.stars);
    var t = state.activeTeam;
    state.teamScores[t] += 1;
    state.activeTeam = (state.activeTeam + 1) % 3;

    var statusEl = document.getElementById("raceStatusTxt");
    if (statusEl) {
      statusEl.innerHTML = "📚 <b>" + TEAMS[t].name + "</b> added Book #" + state.teamScores[t] + "! Next up: <b>" + TEAMS[state.activeTeam].name + "</b>!";
    }
  }

  /* ============ 5. RENDER 10-BOOK LIBRARY SPINES ============ */
  function renderBookSpines() {
    var row = document.getElementById("bookSpinesRow");
    if (!row) return;
    row.innerHTML = STORYBOOKS.map(function(b, idx) {
      var active = (idx === state.bookIdx) ? " active" : "";
      return '<button type="button" class="book-spine-btn' + active + '" data-book="' + idx + '">' + b.shortTitle + '</button>';
    }).join("");

    row.querySelectorAll("[data-book]").forEach(function(btn) {
      btn.addEventListener("click", function() {
        state.bookIdx = parseInt(btn.getAttribute("data-book"), 10);
        resetCurrentBookPuzzle();
        playClip(currentBook().id + "_intro");
        renderAll();
      });
    });
  }

  /* ============ 6. POP-UP STORYBOOK DETAIL MODAL ============ */
  function openDetailModal(stepIdx) {
    state.detailStepIdx = stepIdx;
    var book = currentBook();
    var step = book.steps[stepIdx];
    var meta = SEQ_META[stepIdx];

    var badge = document.getElementById("detailStepBadge");
    badge.textContent = meta.num + ". " + meta.label;
    badge.style.background = meta.color;

    document.getElementById("detailBookTitle").textContent = book.fullTitle;
    document.getElementById("detailStoryText").innerHTML =
      '<span style="color:' + meta.color + ';text-decoration:underline">' + meta.label + ',</span> ' + step.text;

    var rightBox = document.getElementById("detailRightIllustration");
    var preImg = (book.id === "b01") && state.preloadedImages[step.id];
    if (preImg && preImg.complete && preImg.naturalWidth > 0) {
      rightBox.style.background = "#1E293B";
      rightBox.innerHTML =
        '<img src="' + step.imgUrl + '" alt="' + step.label + '" style="max-width:94%;max-height:84%;border-radius:14px;border:4px solid #fff;box-shadow:0 8px 22px rgba(0,0,0,0.4)" />' +
        '<div style="margin-top:8px;font-size:14px;font-weight:900;color:#FEF08A">' + step.emoji + ' ' + step.sceneTag + '</div>';
    } else {
      rightBox.style.background = step.bg;
      rightBox.innerHTML =
        '<div style="font-size:128px;line-height:1;filter:drop-shadow(0 10px 14px rgba(0,0,0,0.2))">' + step.emoji + '</div>' +
        '<div style="margin-top:16px;font-size:22px;font-weight:900;color:#1E293B;background:rgba(255,255,255,0.9);padding:8px 24px;border-radius:999px;border:3px solid ' + meta.color + '">' +
          meta.label + ' &bull; ' + step.sceneTag +
        '</div>';
    }

    document.getElementById("detailModal").classList.add("open");
    playClip(book.id + "_" + step.id);
  }

  /* ============ 7. HANDLE MATCHING A SLOT (Modes 1 & 2) ============ */
  function attemptPlaceIntoSlot(sourceStepIdx, targetSlotIdx) {
    if (state.filledSlots[targetSlotIdx]) return;
    var book = currentBook();
    if (sourceStepIdx === targetSlotIdx) {
      state.filledSlots[targetSlotIdx] = true;
      state.selectedTrayIdx = null;
      awardTeamBook();
      playTone(587.33, 0.14, "sine");
      setTimeout(function() { playTone(880, 0.18, "triangle"); }, 85);
      triggerConfetti();

      var stepKey = book.steps[targetSlotIdx].id;
      playClip(book.id + "_" + stepKey);
      renderAll();
    } else {
      playTone(210, 0.22, "sawtooth");
      playClip("try_again");
      var trayEl = document.querySelector('[data-tray-step="' + sourceStepIdx + '"]');
      if (trayEl) {
        trayEl.classList.add("shake");
        setTimeout(function() { trayEl.classList.remove("shake"); }, 450);
      }
    }
  }

  /* ============ 8. RENDER READING DESK (MODES 1, 2, 3) ============ */
  function renderDeskHeader(book) {
    var allDone = state.filledSlots.every(Boolean);
    return (
      '<div class="desk-header">' +
        '<div class="book-banner-title">' +
          '<span>' + book.icon + '</span> <span>' + book.fullTitle + '</span>' +
        '</div>' +
        '<div class="desk-actions">' +
          '<button type="button" class="lib-btn" id="hearBookIntroBtn"><span>🇺🇸</span> <span>Book Intro</span></button>' +
          '<button type="button" class="lib-btn green" id="readWholeBookBtn"><span>🇬🇧</span> <span>Read Full Story</span></button>' +
          '<button type="button" class="lib-btn purple" id="nextStorybookBtn"><span>➡️</span> <span>Next Book (' + ((state.bookIdx + 1) % STORYBOOKS.length + 1) + '/10)</span></button>' +
        '</div>' +
      '</div>'
    );
  }

  function renderWordsMode(desk, book) {
    var allPlaced = state.filledSlots.every(Boolean);

    var bookmarksHtml = state.trayOrder.map(function(stepIdx) {
      var m = SEQ_META[stepIdx];
      var placed = state.filledSlots[stepIdx];
      var sel = (state.selectedTrayIdx === stepIdx) ? " selected" : "";
      return (
        '<button type="button" class="seq-bookmark-card ' + m.id + (placed ? " placed" : "") + sel + '" ' +
          'draggable="' + (!placed) + '" data-tray-step="' + stepIdx + '">' +
          '<span class="sbc-word">' + m.label + '</span>' +
          '<span class="sbc-sub">' + m.sub + '</span>' +
        '</button>'
      );
    }).join("");

    var slotsHtml = book.steps.map(function(st, idx) {
      var m = SEQ_META[idx];
      var isFilled = state.filledSlots[idx];
      var slotCls = "story-page-slot" + (isFilled ? (" filled " + m.id) : "");

      return (
        '<div class="' + slotCls + '" data-slot-idx="' + idx + '">' +
          '<div class="sps-step-header">' +
            '<span>PAGE ' + (idx + 1) + ' OF 4</span>' +
            '<span>' + (isFilled ? '✅ Complete' : 'Step ' + (idx + 1)) + '</span>' +
          '</div>' +

          '<div class="sps-word-dropzone' + (isFilled ? (' locked ' + m.id) : '') + '">' +
            (isFilled ? (m.num + '. ' + m.label) : '🔖 Drop "' + m.label + '" Here') +
          '</div>' +

          '<div class="sps-illustration-box" style="background:' + st.bg + '">' +
            '<div class="sps-emoji-stage">' + st.emoji + '</div>' +
            '<div class="sps-scene-caption">' + st.sceneTag + '</div>' +
          '</div>' +

          '<div class="sps-story-text">' +
            '<span>' +
              (isFilled ? ('<b class="seq-lead ' + m.id + '">' + m.label + ',</b> ') : '<b style="color:#94A3B8">[ ___ ], </b>') +
              st.text +
            '</span>' +
          '</div>' +

          '<div class="sps-footer-hint">' +
            (isFilled ? '🔍 Click Page to View Picture &amp; Listen!' : '👆 Tap or Drag the Sequence Word!') +
          '</div>' +
        '</div>'
      );
    }).join("");

    desk.innerHTML =
      renderDeskHeader(book) +
      '<div class="bookmark-tray">' +
        '<div class="tray-instruction">' +
          (allPlaced
            ? '🎉 <b>Story Complete!</b> Click any page below to open the Pop-Up Picture Book!'
            : '🔖 <b>Step 1:</b> Drag or tap <b>FIRST, NEXT, THEN, LAST</b> onto the 4 story pages in order!') +
        '</div>' +
        '<div class="draggable-bookmarks">' + bookmarksHtml + '</div>' +
      '</div>' +
      '<div class="four-pages-grid">' + slotsHtml + '</div>';
  }

  function renderCardsMode(desk, book) {
    var cardsTrayHtml = state.trayOrder.map(function(stepIdx) {
      var st = book.steps[stepIdx];
      var placed = state.filledSlots[stepIdx];
      var sel = (state.selectedTrayIdx === stepIdx) ? " selected" : "";
      return (
        '<button type="button" class="seq-bookmark-card" style="background:#FFFBEB;color:#1E293B;border-color:#F59E0B;height:58px;padding:4px 8px" ' +
          'draggable="' + (!placed) + '" data-tray-step="' + stepIdx + '">' +
          '<div style="font-size:18px;line-height:1">' + st.emoji + ' <b style="font-size:12px">' + st.sceneTag + '</b></div>' +
          '<div style="font-size:10px;font-weight:800;color:#78350F;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:165px">' + st.text + '</div>' +
        '</button>'
      );
    }).join("");

    var slotsHtml = book.steps.map(function(st, idx) {
      var m = SEQ_META[idx];
      var isFilled = state.filledSlots[idx];
      var slotCls = "story-page-slot" + (isFilled ? (" filled " + m.id) : "");

      return (
        '<div class="' + slotCls + '" data-slot-idx="' + idx + '">' +
          '<div class="sps-word-dropzone locked ' + m.id + '" style="margin-top:0">' +
            m.num + '. ' + m.label +
          '</div>' +

          (isFilled
            ? (
              '<div class="sps-illustration-box" style="background:' + st.bg + '">' +
                '<div class="sps-emoji-stage">' + st.emoji + '</div>' +
                '<div class="sps-scene-caption">' + st.sceneTag + '</div>' +
              '</div>' +
              '<div class="sps-story-text"><span><b class="seq-lead ' + m.id + '">' + m.label + ',</b> ' + st.text + '</span></div>' +
              '<div class="sps-footer-hint">🔍 Click to Open Picture Book!</div>'
            )
            : (
              '<div style="flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;color:#64748B;font-weight:900;font-size:13px;padding:12px;text-align:center;background:#F8FAFC;border-radius:14px;border:2px dashed #CBD5E1">' +
                '<div style="font-size:38px;margin-bottom:6px">📖❓</div>' +
                '<div>Which story card happens <b>' + m.label + '</b> (Step ' + (idx + 1) + ')?</div>' +
              '</div>'
            )
          ) +
        '</div>'
      );
    }).join("");

    desk.innerHTML =
      renderDeskHeader(book) +
      '<div class="bookmark-tray">' +
        '<div class="tray-instruction">🔀 <b>Scrambled Story Cards:</b> Tap or drag each story scene under <b>FIRST, NEXT, THEN, LAST</b>!</div>' +
        '<div class="draggable-bookmarks">' + cardsTrayHtml + '</div>' +
      '</div>' +
      '<div class="four-pages-grid">' + slotsHtml + '</div>';
  }

  function renderQuizMode(desk, book) {
    var sIdx = state.quizStepIdx % 4;
    var st = book.steps[sIdx];
    var m = SEQ_META[sIdx];

    var dotsHtml = SEQ_META.map(function(meta, i) {
      var cur = (i === sIdx) ? " current" : "";
      return '<div class="tl-dot' + cur + '">Step ' + (i + 1) + (i === sIdx ? " (Here!)" : "") + '</div>';
    }).join(' <span style="font-weight:900;color:#94A3B8">&rarr;</span> ');

    var choicesHtml = SEQ_META.map(function(meta, i) {
      var isRight = state.quizSolved && (i === sIdx);
      return (
        '<button type="button" class="seq-bookmark-card ' + meta.id + '" style="height:68px" data-quiz-pick="' + i + '">' +
          '<span class="sbc-word" style="font-size:24px">' + meta.label + '</span>' +
          '<span class="sbc-sub">' + meta.sub + '</span>' +
        '</button>'
      );
    }).join("");

    desk.innerHTML =
      renderDeskHeader(book) +
      '<div class="detective-card">' +
        '<div class="timeline-stepper">' + dotsHtml + '</div>' +
        '<div style="display:flex;align-items:center;gap:20px;background:' + st.bg + ';padding:14px 26px;border-radius:20px;border:3px solid #CBD5E1">' +
          '<div style="font-size:70px;line-height:1">' + st.emoji + '</div>' +
          '<div style="font-size:24px;font-weight:900;color:#1E293B;max-width:560px;line-height:1.35">' +
            (state.quizSolved
              ? ('<span style="background:' + m.color + ';color:#fff;padding:2px 12px;border-radius:10px">' + m.label + ',</span> ')
              : '<span style="background:#FEF08A;border:3px dashed #D97706;padding:2px 18px;border-radius:10px;color:#92400E">___?___</span> '
            ) +
            st.text +
          '</div>' +
        '</div>' +
        '<div style="font-size:14px;font-weight:900;color:#475569">' +
          (state.quizSolved
            ? '✅ Correct! Tap "Next Detective Card" below for another chapter!'
            : '🕵️ Look at the timeline step above! Which Sequence Word begins this sentence?') +
        '</div>' +
      '</div>' +
      '<div class="draggable-bookmarks" style="flex:0">' + choicesHtml + '</div>';
  }

  function renderDesk() {
    var desk = document.getElementById("readingDesk");
    if (!desk) return;
    var book = currentBook();

    if (state.mode === "cards") renderCardsMode(desk, book);
    else if (state.mode === "quiz") renderQuizMode(desk, book);
    else renderWordsMode(desk, book);

    // Wire header buttons
    var introBtn = document.getElementById("hearBookIntroBtn");
    if (introBtn) {
      introBtn.addEventListener("click", function() {
        playClip(book.id + "_intro");
      });
    }

    var readAllBtn = document.getElementById("readWholeBookBtn");
    if (readAllBtn) {
      readAllBtn.addEventListener("click", function() {
        playClips([
          book.id + "_first",
          book.id + "_next",
          book.id + "_then",
          book.id + "_last"
        ]);
      });
    }

    var nextBookBtn = document.getElementById("nextStorybookBtn");
    if (nextBookBtn) {
      nextBookBtn.addEventListener("click", function() {
        state.bookIdx = (state.bookIdx + 1) % STORYBOOKS.length;
        resetCurrentBookPuzzle();
        playClip(currentBook().id + "_intro");
        renderAll();
      });
    }

    // Wire Tray Bookmarks (Click + Drag)
    desk.querySelectorAll("[data-tray-step]").forEach(function(el) {
      var stepIdx = parseInt(el.getAttribute("data-tray-step"), 10);
      if (state.filledSlots[stepIdx]) return;

      el.addEventListener("click", function() {
        state.selectedTrayIdx = stepIdx;
        playClip("rule_" + SEQ_META[stepIdx].id);
        renderDesk();
      });

      el.addEventListener("dragstart", function(e) {
        state.selectedTrayIdx = stepIdx;
        if (e.dataTransfer) e.dataTransfer.setData("text/plain", String(stepIdx));
      });
    });

    // Wire Story Slots (Click + Drop + Open Detail View)
    desk.querySelectorAll("[data-slot-idx]").forEach(function(slotEl) {
      var slotIdx = parseInt(slotEl.getAttribute("data-slot-idx"), 10);

      slotEl.addEventListener("dragover", function(e) {
        e.preventDefault();
      });

      slotEl.addEventListener("drop", function(e) {
        e.preventDefault();
        var srcIdx = state.selectedTrayIdx;
        if (e.dataTransfer) {
          var dt = parseInt(e.dataTransfer.getData("text/plain"), 10);
          if (!isNaN(dt)) srcIdx = dt;
        }
        if (srcIdx !== null && srcIdx !== undefined) {
          attemptPlaceIntoSlot(srcIdx, slotIdx);
        }
      });

      slotEl.addEventListener("click", function() {
        if (state.filledSlots[slotIdx]) {
          openDetailModal(slotIdx);
        } else if (state.selectedTrayIdx !== null) {
          attemptPlaceIntoSlot(state.selectedTrayIdx, slotIdx);
        } else {
          // If nothing selected in tray, tapping the matching slot can guide or play rule
          playClip("rule_" + SEQ_META[slotIdx].id);
        }
      });
    });

    // Wire Mode 3 Detective choices
    desk.querySelectorAll("[data-quiz-pick]").forEach(function(btn) {
      btn.addEventListener("click", function() {
        var picked = parseInt(btn.getAttribute("data-quiz-pick"), 10);
        var target = state.quizStepIdx % 4;
        if (picked === target) {
          state.quizSolved = true;
          awardTeamBook();
          triggerConfetti();
          playClip(book.id + "_" + SEQ_META[target].id);
          renderAll();
          setTimeout(function() {
            state.quizStepIdx = (state.quizStepIdx + 1) % 4;
            if (state.quizStepIdx === 0) {
              state.bookIdx = (state.bookIdx + 1) % STORYBOOKS.length;
            }
            state.quizSolved = false;
            renderAll();
          }, 2900);
        } else {
          playTone(210, 0.22, "sawtooth");
          playClip("try_again");
        }
      });
    });
  }

  /* ============ 9. RENDER RIGHT 3-TEAM BOOKWORM STACK RACE ============ */
  function renderRaceStacks() {
    var grid = document.getElementById("bookStacksGrid");
    if (!grid) return;

    grid.innerHTML = TEAMS.map(function(t, idx) {
      var sc = state.teamScores[idx];
      var isTurn = (state.activeTeam === idx);
      var booksHtml = "";
      for (var b = 0; b < Math.min(sc, 16); b++) {
        var col = t.bookColors[b % t.bookColors.length];
        booksHtml += '<div class="stacked-book" style="background:' + col + '">' + (b + 1) + '</div>';
      }

      return (
        '<div class="team-pedestal-col' + (isTurn ? ' active-turn' : '') + '" data-team-col="' + idx + '">' +
          '<div class="tp-badge" style="border-color:' + t.c + '">' +
            '<div style="font-size:11.5px;font-weight:900;color:' + t.c + '">' + t.icon + ' ' + t.name + '</div>' +
            '<div style="font-size:13px;font-weight:900">📚 ' + sc + '</div>' +
          '</div>' +
          '<div class="stack-column">' +
            '<div class="oak-shelf-base"></div>' +
            booksHtml +
          '</div>' +
          '<button type="button" class="lib-btn" data-add-book="' + idx + '" style="width:100%;justify-content:center;padding:4px 2px;font-size:11px">+1 Book</button>' +
        '</div>'
      );
    }).join("");

    grid.querySelectorAll("[data-team-col]").forEach(function(col) {
      col.addEventListener("click", function(e) {
        if (e.target.closest("button")) return;
        state.activeTeam = parseInt(col.getAttribute("data-team-col"), 10);
        renderRaceStacks();
      });
    });

    grid.querySelectorAll("[data-add-book]").forEach(function(btn) {
      btn.addEventListener("click", function(e) {
        e.stopPropagation();
        var ti = parseInt(btn.getAttribute("data-add-book"), 10);
        state.teamScores[ti] += 1;
        playClip("praise_" + (ti + 1));
        renderRaceStacks();
      });
    });
  }

  function renderAll() {
    renderBookSpines();
    renderDesk();
    renderRaceStacks();
  }

  /* ============ 10. WIRE GLOBAL CONTROLS ============ */
  document.querySelectorAll(".mode-tab").forEach(function(tab) {
    tab.addEventListener("click", function() {
      state.mode = tab.getAttribute("data-mode");
      resetCurrentBookPuzzle();
      document.querySelectorAll(".mode-tab").forEach(function(t) {
        t.classList.toggle("active", t === tab);
      });
      if (state.mode === "words") playClip("mode_words");
      else if (state.mode === "cards") playClip("mode_cards");
      else if (state.mode === "quiz") playClip("mode_quiz");
      renderAll();
    });
  });

  document.querySelectorAll("[data-rule]").forEach(function(btn) {
    btn.addEventListener("click", function() {
      playClip("rule_" + btn.getAttribute("data-rule"));
    });
  });

  document.getElementById("detailReadBtn").addEventListener("click", function() {
    var book = currentBook();
    var step = book.steps[state.detailStepIdx];
    playClip(book.id + "_" + step.id);
  });

  document.getElementById("detailReadAllBtn").addEventListener("click", function() {
    var book = currentBook();
    playClips([
      book.id + "_first",
      book.id + "_next",
      book.id + "_then",
      book.id + "_last"
    ]);
  });

  document.getElementById("detailCloseBtn").addEventListener("click", function() {
    document.getElementById("detailModal").classList.remove("open");
  });

  document.getElementById("repeatBtn").addEventListener("click", function() {
    playClip(currentBook().id + "_intro");
  });

  document.getElementById("resetBtn").addEventListener("click", function() {
    state.teamScores = [0, 0, 0];
    state.stars = 0;
    document.getElementById("starCount").textContent = "0";
    resetCurrentBookPuzzle();
    renderAll();
  });

  document.getElementById("soundBtn").addEventListener("click", function() {
    state.soundOn = !state.soundOn;
    if (!state.soundOn) stopAllSpeech();
    document.getElementById("soundIcon").textContent = state.soundOn ? "🔊" : "🔇";
  });

  document.getElementById("fullBtn").addEventListener("click", function() {
    var el = document.documentElement;
    if (!document.fullscreenElement) {
      if (el.requestFullscreen) el.requestFullscreen().catch(function() {});
    } else {
      if (document.exitFullscreen) document.exitFullscreen().catch(function() {});
    }
  });

  document.getElementById("whoWinsBtn").addEventListener("click", function() {
    var max = Math.max.apply(null, state.teamScores);
    var winners = TEAMS.filter(function(_, i) { return state.teamScores[i] === max; });
    var names = winners.map(function(w) { return w.icon + " " + w.name; }).join(" & ");
    document.getElementById("winnerText").textContent = max === 0
      ? "Stack some storybooks first to crown a winner!"
      : names + " (" + max + " Books!)";
    document.getElementById("winnerModal").classList.add("open");
    triggerConfetti();
    playClip("winner");
  });

  document.getElementById("closeWinnerBtn").addEventListener("click", function() {
    document.getElementById("winnerModal").classList.remove("open");
  });

  document.getElementById("newRaceBtn").addEventListener("click", function() {
    state.teamScores = [0, 0, 0];
    document.getElementById("winnerModal").classList.remove("open");
    renderRaceStacks();
  });

  resetCurrentBookPuzzle();
  renderAll();
})();
</script>
</body>
</html>