ซอร์สแพ็กเกจ · Belter Noun or Verb Skelter

CC BY 4.0 · โปรดให้เครดิต @joe · v1

← รายละเอียดเกม

นำไปใช้ รีมิกซ์ แล้วนำไปสอน

โค้ดด้านล่างเป็นเพียง HTML ทางเข้าที่เล่นได้ของแพ็กเกจ ดาวน์โหลด ZIP ทั้งชุดเพื่อแก้หรือรีมิกซ์เกมพร้อมไฟล์ประกอบแบบเส้นทางสัมพัทธ์ทั้งหมด

⬇ ดาวน์โหลด HTML ทางเข้า ⬇ ดาวน์โหลดแพ็กเกจทั้งหมด
พรอมต์เริ่มต้นสำหรับแก้ด้วย AI
แก้เกมห้องเรียนนี้สำหรับกระดานโต้ตอบ คงเป้าแตะขนาดใหญ่และการเล่นทั้งห้อง ห้ามการติดตาม การเข้าสู่ระบบ การเก็บข้อมูลส่วนบุคคล และการเรียก API ระยะไกล ใช้ ZIP ที่ดาวน์โหลดทั้งชุด ไม่ใช่แค่ HTML ทางเข้าที่แสดงด้านล่าง และคงโครงสร้างโฟลเดอร์ที่พร้อมใช้ในเบราว์เซอร์ คงเป้าหมายทางการศึกษา ผู้สร้างต้นฉบับ: @joe บน ClassBoard Games (CC BY 4.0)
เฉพาะ HTML ทางเข้า · index.html · 2349.8 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>Belter Noun or Verb Skelter — Kindergarten Conveyor Game</title>
<style>
  :root {
    --app-scale: 1;
    --ink: #1E293B;
    --noun-c: #2563EB;
    --noun-dark: #1E3A8A;
    --verb-c: #EA580C;
    --verb-dark: #9A3412;
    --ride-ms: 7000ms;
  }

  * {
    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 30% 18%, #38BDF8 0%, #1D4ED8 52%, #0F172A 100%);
    color: var(--ink);
  }

  .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, #BAE6FD 0%, #E0F2FE 54%, #BBF7D0 54%, #4ADE80 100%);
    border-radius: 30px;
    border: 6px solid #F59E0B;
    box-shadow: 0 25px 65px rgba(15, 23, 42, 0.65), inset 0 2px 0 rgba(255,255,255,0.9);
    display: flex;
    flex-direction: column;
    padding: 12px 16px;
    gap: 9px;
    position: relative;
    overflow: hidden;
  }

  /* Decorative sky clouds & sun */
  .sky-decor {
    position: absolute;
    inset: 0;
    pointer-events: none;
    z-index: 0;
    overflow: hidden;
  }
  .sun-orb {
    position: absolute;
    top: 68px;
    right: 36px;
    font-size: 56px;
    filter: drop-shadow(0 0 18px rgba(250, 204, 21, 0.8));
    animation: gentleFloat 4s ease-in-out infinite;
  }
  @keyframes gentleFloat {
    0%, 100% { transform: translateY(0) scale(1); }
    50% { transform: translateY(-6px) scale(1.04); }
  }

  /* ============ 1. TOP HUD BAR ============ */
  .top-hud {
    position: relative;
    z-index: 10;
    height: 58px;
    background: linear-gradient(90deg, #1E3A8A 0%, #2563EB 45%, #EA580C 100%);
    border-radius: 18px;
    border: 3px solid #FDE047;
    box-shadow: 0 6px 0 rgba(15, 23, 42, 0.35);
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 0 14px;
    color: #fff;
    flex-shrink: 0;
  }

  .brand-left {
    display: flex;
    align-items: center;
    gap: 10px;
  }
  .brand-icon {
    width: 42px;
    height: 42px;
    border-radius: 13px;
    background: linear-gradient(135deg, #FDE047, #F59E0B);
    border: 3px solid #FFFBEB;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 24px;
    box-shadow: 0 3px 0 rgba(120, 53, 15, 0.4);
  }
  .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.35);
  }
  .brand-sub {
    font-size: 11px;
    font-weight: 800;
    color: #FEF08A;
  }

  /* Speed & Round Controls in HUD */
  .hud-center {
    display: flex;
    align-items: center;
    gap: 8px;
    background: rgba(15, 23, 42, 0.32);
    padding: 4px 8px;
    border-radius: 14px;
    border: 2px solid rgba(255,255,255,0.24);
  }

  .speed-btn {
    border: none;
    background: transparent;
    color: #E0F2FE;
    font-family: inherit;
    font-size: 12.5px;
    font-weight: 900;
    padding: 5px 10px;
    border-radius: 9px;
    cursor: pointer;
    transition: all 0.14s;
  }
  .speed-btn:hover { background: rgba(255,255,255,0.14); color: #fff; }
  .speed-btn.active {
    background: linear-gradient(180deg, #FDE047, #F59E0B);
    color: #451A03;
    box-shadow: 0 2px 0 #B45309;
  }

  .hud-sep {
    width: 2px;
    height: 22px;
    background: rgba(255,255,255,0.25);
  }

  .action-chip {
    border: 2px solid rgba(255,255,255,0.65);
    background: rgba(255,255,255,0.18);
    color: #fff;
    font-family: inherit;
    font-size: 12.5px;
    font-weight: 900;
    padding: 5px 10px;
    border-radius: 10px;
    cursor: pointer;
    display: flex;
    align-items: center;
    gap: 4px;
  }
  .action-chip:hover { background: rgba(255,255,255,0.3); }
  .action-chip.pause-active {
    background: #EF4444;
    border-color: #FCA5A5;
  }

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

  .util-btn {
    border: 2px solid rgba(255,255,255,0.55);
    background: rgba(255,255,255,0.16);
    color: #fff;
    font-family: inherit;
    font-size: 12.5px;
    font-weight: 900;
    padding: 6px 10px;
    border-radius: 11px;
    cursor: pointer;
    display: flex;
    align-items: center;
    gap: 4px;
  }
  .util-btn:hover { background: rgba(255,255,255,0.28); }

  /* ============ 2. TEAM SCOREBOARD & TURN BANNER STRIP ============ */
  .teams-strip {
    position: relative;
    z-index: 10;
    height: 68px;
    display: grid;
    grid-template-columns: 1fr 310px;
    gap: 10px;
    flex-shrink: 0;
  }

  .team-cards-row {
    display: grid;
    grid-template-columns: repeat(var(--team-count, 3), 1fr);
    gap: 8px;
  }

  .team-score-card {
    background: rgba(255, 255, 255, 0.94);
    border: 3.5px solid #CBD5E1;
    border-radius: 16px;
    padding: 6px 10px;
    display: flex;
    align-items: center;
    justify-content: space-between;
    position: relative;
    cursor: pointer;
    transition: transform 0.16s, box-shadow 0.16s, border-color 0.16s;
  }

  .team-score-card.current-turn {
    transform: translateY(-2px) scale(1.01);
    box-shadow: 0 6px 0 rgba(0,0,0,0.2), 0 0 0 4px #FDE047;
    background: #FFFFFF;
  }

  .team-score-card.bump {
    animation: scoreBump 0.45s cubic-bezier(0.34, 1.56, 0.64, 1);
  }
  @keyframes scoreBump {
    0%, 100% { transform: scale(1); }
    45% { transform: scale(1.12); }
  }

  .tsc-left {
    display: flex;
    align-items: center;
    gap: 8px;
  }

  .tsc-avatar {
    width: 42px;
    height: 42px;
    border-radius: 12px;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 26px;
    color: #fff;
    font-weight: 900;
    box-shadow: inset 0 -3px 0 rgba(0,0,0,0.2);
  }

  .tsc-name {
    font-size: 14.5px;
    font-weight: 900;
    color: #0F172A;
    line-height: 1.1;
  }

  .tsc-sub {
    font-size: 11px;
    font-weight: 800;
    color: #475569;
    margin-top: 2px;
  }

  .tsc-right {
    text-align: right;
  }
  .tsc-pts {
    font-size: 24px;
    font-weight: 900;
    line-height: 1;
    font-variant-numeric: tabular-nums;
  }
  .tsc-turn-pill {
    display: inline-block;
    font-size: 10px;
    font-weight: 900;
    padding: 2px 7px;
    border-radius: 999px;
    background: #FDE047;
    color: #451A03;
    text-transform: uppercase;
  }

  /* Round Progress & Sorted Bins Box */
  .round-progress-card {
    background: rgba(255, 255, 255, 0.94);
    border: 3.5px solid #93C5FD;
    border-radius: 16px;
    padding: 6px 12px;
    display: flex;
    flex-direction: column;
    justify-content: center;
    gap: 4px;
  }

  .rp-top {
    display: flex;
    align-items: center;
    justify-content: space-between;
    font-size: 12.5px;
    font-weight: 900;
    color: #1E3A8A;
  }

  .rp-bins {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 6px;
  }

  .rp-bin-badge {
    flex: 1;
    text-align: center;
    padding: 3px 6px;
    border-radius: 9px;
    font-size: 12px;
    font-weight: 900;
  }
  .rp-bin-badge.n { background: #DBEAFE; color: #1E40AF; border: 1.5px solid #93C5FD; }
  .rp-bin-badge.v { background: #FFEDD5; color: #9A3412; border: 1.5px solid #FDBA74; }

  /* ============ 3. CONVEYOR BELT & HELTER-SKELTER FACTORY STAGE ============ */
  .conveyor-arena {
    position: relative;
    z-index: 10;
    flex: 1;
    min-height: 0;
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    gap: 10px;
  }

  /* Upper Conveyor Machine Box */
  .machine-frame {
    position: relative;
    flex: 1;
    background: linear-gradient(180deg, rgba(255,255,255,0.88) 0%, rgba(240,249,255,0.92) 72%, #94A3B8 72%, #64748B 100%);
    border-radius: 26px;
    border: 5px solid #475569;
    box-shadow: 0 14px 30px rgba(15, 23, 42, 0.22), inset 0 2px 0 #fff;
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    overflow: hidden;
  }

  .machine-frame.shake {
    animation: machineShake 0.45s ease;
  }
  @keyframes machineShake {
    0%, 100% { transform: translateX(0); }
    20% { transform: translateX(-11px); }
    40% { transform: translateX(11px); }
    60% { transform: translateX(-7px); }
    80% { transform: translateX(7px); }
  }

  /* Top Draining Countdown Timer Bar */
  .timer-bar-wrap {
    height: 16px;
    width: 100%;
    background: #E2E8F0;
    border-bottom: 3px solid #475569;
    position: relative;
    overflow: hidden;
  }

  .timer-bar-fill {
    height: 100%;
    width: 100%;
    background: linear-gradient(90deg, #EF4444 0%, #F59E0B 45%, #10B981 100%);
    transform-origin: left center;
  }

  .timer-bar-fill.draining {
    animation: drainClock var(--ride-ms) linear forwards;
  }
  .timer-bar-fill.paused {
    animation-play-state: paused !important;
  }

  @keyframes drainClock {
    from { transform: scaleX(1); }
    to { transform: scaleX(0); }
  }

  /* Turn &Worked Sentence Reveal Header inside Machine */
  .machine-header-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 8px 18px 0;
    position: relative;
    z-index: 6;
  }

  .turn-banner-pill {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 5px 15px;
    border-radius: 999px;
    background: #1E293B;
    color: #FEF08A;
    font-size: 15px;
    font-weight: 900;
    border: 3px solid #FACC15;
    box-shadow: 0 4px 10px rgba(0,0,0,0.15);
  }

  .sweet-spot-hint {
    font-size: 13px;
    font-weight: 900;
    color: #0F172A;
    background: rgba(254, 240, 138, 0.9);
    border: 2px solid #F59E0B;
    padding: 4px 14px;
    border-radius: 999px;
  }

  /* Worked sentence reveal overlay pill */
  .reveal-card {
    position: absolute;
    top: 46px;
    left: 50%;
    transform: translateX(-50%);
    z-index: 15;
    background: #FFFFFF;
    border: 4px solid #F59E0B;
    border-radius: 20px;
    padding: 6px 22px;
    display: none;
    align-items: center;
    gap: 12px;
    box-shadow: 0 10px 25px rgba(15, 23, 42, 0.25);
    white-space: nowrap;
  }
  .reveal-card.show {
    display: inline-flex;
    animation: popIn 0.28s cubic-bezier(0.34, 1.56, 0.64, 1);
  }
  @keyframes popIn {
    0% { transform: translateX(-50%) scale(0.7); opacity: 0; }
    100% { transform: translateX(-50%) scale(1); opacity: 1; }
  }

  .rv-badge {
    padding: 4px 12px;
    border-radius: 12px;
    font-size: 16px;
    font-weight: 900;
    color: #fff;
  }
  .rv-badge.noun { background: var(--noun-c); }
  .rv-badge.verb { background: var(--verb-c); }

  .rv-sentence {
    font-size: 19px;
    font-weight: 900;
    color: #1E293B;
  }

  /* Left & Right Factory Hatch Doors + Sweet Spot Arch */
  .belt-viewport {
    position: relative;
    flex: 1;
    width: 100%;
    display: flex;
    align-items: center;
    justify-content: center;
    overflow: hidden;
  }

  /* Golden Sweet Spot Target Zone in the middle of the belt */
  .sweet-spot-zone {
    position: absolute;
    top: 10px;
    bottom: 6px;
    width: 340px;
    left: 50%;
    transform: translateX(-50%);
    border: 3px dashed rgba(245, 158, 11, 0.55);
    background: radial-gradient(circle, rgba(254, 240, 138, 0.28) 0%, transparent 75%);
    border-radius: 22px;
    pointer-events: none;
    display: flex;
    align-items: flex-end;
    justify-content: center;
    padding-bottom: 6px;
    z-index: 1;
  }
  .sweet-spot-zone span {
    font-size: 11px;
    font-weight: 900;
    color: #B45309;
    letter-spacing: 0.6px;
    text-transform: uppercase;
    background: rgba(254, 243, 199, 0.9);
    padding: 2px 10px;
    border-radius: 999px;
  }

  /* Left & Right Helter-Skelter Side Chute Portals */
  .side-portal {
    position: absolute;
    top: 12px;
    bottom: 8px;
    width: 96px;
    z-index: 4;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    font-weight: 900;
    font-size: 12px;
    text-align: center;
    pointer-events: none;
  }
  .side-portal.left-chute {
    left: 10px;
    background: linear-gradient(90deg, rgba(239, 68, 68, 0.22), transparent);
    border-left: 5px solid #EF4444;
    border-radius: 14px 0 0 14px;
    color: #991B1B;
  }
  .side-portal.right-entry {
    right: 10px;
    background: linear-gradient(-90deg, rgba(37, 99, 235, 0.2), transparent);
    border-right: 5px solid #3B82F6;
    border-radius: 0 14px 14px 0;
    color: #1E3A8A;
  }

  /* THE MOVING RIDER & CRATE ON THE CONVEYOR BELT! */
  .crate-rider {
    position: relative;
    z-index: 8;
    will-change: transform;
  }

  /* Just like original conveyor-quest-kindergarten:
     Enters from right (+520px), slows through center so children can read it,
     then exits off the left edge (-560px)! */
  .crate-rider.riding {
    animation: rideBeltAcross var(--ride-ms) linear forwards;
  }
  .crate-rider.paused {
    animation-play-state: paused !important;
  }

  @keyframes rideBeltAcross {
    0%   { transform: translateX(520px); }
    18%  { transform: translateX(170px); }
    50%  { transform: translateX(0px); }
    82%  { transform: translateX(-170px); }
    100% { transform: translateX(-560px); }
  }

  .word-crate {
    width: 268px;
    height: 162px;
    background: linear-gradient(180deg, #FFFBEB 0%, #FDE68A 100%);
    border: 5px solid #B45309;
    border-radius: 26px;
    box-shadow: 0 10px 0 #78350F, 0 14px 28px rgba(0,0,0,0.25);
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    position: relative;
    cursor: pointer;
    transition: border-color 0.2s, background 0.2s;
  }

  .word-crate.right-noun {
    background: linear-gradient(180deg, #DBEAFE, #93C5FD);
    border-color: #1D4ED8;
    box-shadow: 0 10px 0 #1E3A8A, 0 0 25px rgba(37, 99, 235, 0.55);
  }
  .word-crate.right-verb {
    background: linear-gradient(180deg, #FFEDD5, #FDBA74);
    border-color: #C2410C;
    box-shadow: 0 10px 0 #7C2D12, 0 0 25px rgba(234, 88, 12, 0.55);
  }
  .word-crate.wrong-hit {
    background: linear-gradient(180deg, #FEE2E2, #FCA5A5);
    border-color: #DC2626;
    box-shadow: 0 10px 0 #7F1D1D;
  }

  .crate-inner-frame {
    position: absolute;
    inset: 7px;
    border: 2.5px dashed rgba(180, 83, 9, 0.32);
    border-radius: 18px;
    pointer-events: none;
  }

  .crate-q-badge {
    position: absolute;
    top: -12px;
    right: -12px;
    width: 36px;
    height: 36px;
    border-radius: 50%;
    background: #F59E0B;
    border: 3px solid #FFF;
    color: #fff;
    font-size: 20px;
    font-weight: 900;
    display: flex;
    align-items: center;
    justify-content: center;
    box-shadow: 0 4px 8px rgba(0,0,0,0.2);
  }

  .crate-pic {
    font-size: 68px;
    line-height: 1;
    filter: drop-shadow(0 4px 4px rgba(0,0,0,0.16));
  }

  .crate-word-txt {
    font-size: 40px;
    font-weight: 900;
    color: #0F172A;
    line-height: 1.05;
    margin-top: 2px;
    letter-spacing: 0.5px;
  }

  /* Stamp Overlay on Crate */
  .verdict-stamp {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    pointer-events: none;
    z-index: 12;
    animation: stampSlam 0.3s cubic-bezier(0.34, 1.56, 0.64, 1) forwards;
  }
  @keyframes stampSlam {
    0% { transform: scale(2.2) rotate(-14deg); opacity: 0; }
    100% { transform: scale(1) rotate(-6deg); opacity: 1; }
  }

  .stamp-pill {
    padding: 6px 20px;
    border-radius: 16px;
    border: 4px solid #FFFFFF;
    font-size: 28px;
    font-weight: 900;
    color: #FFFFFF;
    box-shadow: 0 8px 20px rgba(0,0,0,0.35);
  }
  .stamp-pill.ok { background: #16A34A; }
  .stamp-pill.no { background: #DC2626; }

  /* Floating Points (+95 🔥) */
  .floating-score {
    position: absolute;
    top: -24px;
    left: 50%;
    transform: translateX(-50%);
    font-size: 30px;
    font-weight: 900;
    color: #15803D;
    background: #FEF08A;
    border: 3px solid #F59E0B;
    padding: 3px 14px;
    border-radius: 999px;
    box-shadow: 0 6px 14px rgba(0,0,0,0.2);
    z-index: 20;
    white-space: nowrap;
    animation: floatUpPts 1.1s ease-out forwards;
  }
  .floating-score.zero {
    color: #B91C1C;
    background: #FEE2E2;
    border-color: #EF4444;
  }
  @keyframes floatUpPts {
    0%   { opacity: 0; transform: translate(-50%, 10px) scale(0.7); }
    25%  { opacity: 1; transform: translate(-50%, -12px) scale(1.08); }
    100% { opacity: 0; transform: translate(-50%, -56px) scale(0.95); }
  }

  /* Get-Ready Turn Countdown Overlay inside Belt Viewport */
  .get-ready-card {
    position: absolute;
    z-index: 14;
    background: rgba(255, 255, 255, 0.97);
    border: 5px solid #F59E0B;
    border-radius: 26px;
    padding: 14px 34px;
    text-align: center;
    box-shadow: 0 16px 36px rgba(15, 23, 42, 0.3);
    animation: popInCenter 0.25s cubic-bezier(0.34, 1.56, 0.64, 1);
  }
  @keyframes popInCenter {
    0% { transform: scale(0.6); opacity: 0; }
    100% { transform: scale(1); opacity: 1; }
  }

  /* Conveyor Belt Surface & Rollers */
  .conveyor-deck {
    height: 54px;
    width: 100%;
    background: #334155;
    border-top: 6px solid #1E293B;
    position: relative;
    display: flex;
    align-items: center;
    justify-content: space-around;
    padding: 0 24px;
    flex-shrink: 0;
  }

  .conveyor-treads {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    height: 11px;
    background: repeating-linear-gradient(
      90deg,
      #FACC15 0px,
      #FACC15 24px,
      #475569 24px,
      #475569 48px
    );
    animation: beltMoveLeft 0.65s linear infinite;
  }
  .conveyor-treads.paused {
    animation-play-state: paused !important;
  }
  @keyframes beltMoveLeft {
    from { background-position: 0 0; }
    to   { background-position: -48px 0; }
  }

  .roller-wheel {
    width: 32px;
    height: 32px;
    border-radius: 50%;
    background: radial-gradient(circle, #FDE047 30%, #94A3B8 34%, #475569 100%);
    border: 3.5px solid #0F172A;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 14px;
    animation: wheelSpinLeft 1.2s linear infinite;
    z-index: 2;
  }
  .roller-wheel.paused {
    animation-play-state: paused !important;
  }
  @keyframes wheelSpinLeft {
    from { transform: rotate(360deg); }
    to   { transform: rotate(0deg); }
  }

  /* ============ 4. TWO GIANT HELTER-SKELTER ANSWER BUTTONS ============ */
  .answer-chutes-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 18px;
    height: 158px;
    flex-shrink: 0;
  }

  .ans-chute-btn {
    border-radius: 26px;
    border: 6px solid;
    font-family: inherit;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 14px 22px;
    position: relative;
    transition: transform 0.12s cubic-bezier(0.34, 1.56, 0.64, 1), box-shadow 0.12s, filter 0.12s;
  }

  .ans-chute-btn:hover:not(:disabled) {
    transform: translateY(-4px) scale(1.01);
  }
  .ans-chute-btn:active:not(:disabled) {
    transform: translateY(5px);
  }
  .ans-chute-btn:disabled {
    opacity: 0.72;
    cursor: default;
  }

  .ans-chute-btn.noun {
    background: linear-gradient(135deg, #2563EB 0%, #1D4ED8 65%, #1E3A8A 100%);
    border-color: #93C5FD;
    color: #FFFFFF;
    box-shadow: 0 10px 0 #172554;
  }

  .ans-chute-btn.verb {
    background: linear-gradient(135deg, #EA580C 0%, #C2410C 65%, #7C2D12 100%);
    border-color: #FDBA74;
    color: #FFFFFF;
    box-shadow: 0 10px 0 #431407;
  }

  .ans-chute-btn.hit-right {
    box-shadow: 0 0 0 6px #4ADE80, 0 10px 0 #14532D !important;
    filter: brightness(1.12);
  }
  .ans-chute-btn.hit-wrong {
    box-shadow: 0 0 0 6px #EF4444, 0 10px 0 #7F1D1D !important;
    filter: grayscale(0.35);
  }

  .ac-slide-badge {
    width: 92px;
    height: 92px;
    border-radius: 22px;
    background: rgba(255, 255, 255, 0.2);
    border: 3px solid rgba(255, 255, 255, 0.6);
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    font-size: 42px;
    flex-shrink: 0;
  }

  .ac-text {
    flex: 1;
    padding: 0 16px;
    text-align: left;
  }
  .ac-title {
    font-size: 42px;
    font-weight: 900;
    line-height: 1;
    letter-spacing: 0.8px;
    text-shadow: 0 3px 6px rgba(0,0,0,0.3);
  }
  .ac-desc {
    font-size: 15px;
    font-weight: 800;
    margin-top: 5px;
    color: #FEF08A;
  }
  .ac-examples {
    display: inline-block;
    margin-top: 6px;
    background: rgba(15, 23, 42, 0.32);
    padding: 3px 12px;
    border-radius: 999px;
    font-size: 13px;
    font-weight: 800;
    color: #fff;
  }

  .key-hint-pill {
    position: absolute;
    top: 10px;
    right: 14px;
    background: rgba(255,255,255,0.22);
    border: 1.5px solid rgba(255,255,255,0.5);
    padding: 2px 9px;
    border-radius: 8px;
    font-size: 11.5px;
    font-weight: 900;
  }

  /* ============ 5. START / SETUP LAUNCH OVERLAY & PODIUM MODAL ============ */
  .overlay-screen {
    position: absolute;
    inset: 0;
    background: rgba(15, 23, 42, 0.76);
    backdrop-filter: blur(6px);
    z-index: 60;
    display: none;
    align-items: center;
    justify-content: center;
    padding: 20px;
  }
  .overlay-screen.open {
    display: flex;
  }

  .dialog-card {
    width: 100%;
    max-width: 720px;
    background: linear-gradient(180deg, #FFFBEB 0%, #EFF6FF 100%);
    border: 6px solid #F59E0B;
    border-radius: 30px;
    padding: 24px 30px;
    box-shadow: 0 25px 65px rgba(0,0,0,0.55);
    text-align: center;
  }

  .setup-grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 12px;
    margin: 16px 0;
  }

  .setup-box {
    background: #FFFFFF;
    border: 3px solid #93C5FD;
    border-radius: 18px;
    padding: 12px;
  }
  .sb-title {
    font-size: 13.5px;
    font-weight: 900;
    color: #1E3A8A;
    margin-bottom: 8px;
  }
  .sb-opts {
    display: flex;
    gap: 6px;
    justify-content: center;
  }
  .sb-opt {
    flex: 1;
    border: 2.5px solid #CBD5E1;
    background: #F8FAFC;
    color: #334155;
    font-family: inherit;
    font-size: 13px;
    font-weight: 900;
    padding: 7px 6px;
    border-radius: 11px;
    cursor: pointer;
  }
  .sb-opt.active {
    background: #2563EB;
    border-color: #1E3A8A;
    color: #fff;
    box-shadow: 0 3px 0 #1E3A8A;
  }

  .big-start-btn {
    border: 4px solid #B45309;
    background: linear-gradient(180deg, #FDE047 0%, #F59E0B 100%);
    color: #451A03;
    font-family: inherit;
    font-size: 26px;
    font-weight: 900;
    padding: 12px 36px;
    border-radius: 22px;
    box-shadow: 0 8px 0 #92400E;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    gap: 10px;
    transition: transform 0.12s;
  }
  .big-start-btn:hover { transform: translateY(-2px) scale(1.02); }
  .big-start-btn:active { transform: translateY(4px); box-shadow: 0 3px 0 #92400E; }

  /* Podium Rows in End-of-Round Screen */
  .podium-list {
    display: flex;
    flex-direction: column;
    gap: 8px;
    margin: 12px 0;
    text-align: left;
  }
  .podium-row {
    background: #FFFFFF;
    border: 3px solid #93C5FD;
    border-radius: 16px;
    padding: 8px 14px;
    display: flex;
    align-items: center;
    justify-content: space-between;
  }

  .missed-chips {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    justify-content: center;
    margin-top: 6px;
  }
  .missed-chip {
    border: 2px solid #CBD5E1;
    background: #FFFFFF;
    padding: 4px 10px;
    border-radius: 999px;
    font-size: 13px;
    font-weight: 900;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    gap: 5px;
  }

  /* Confetti Layer */
  .confetti-layer {
    position: absolute;
    inset: 0;
    pointer-events: none;
    z-index: 70;
    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="sky-decor">
      <div class="sun-orb">☀️</div>
    </div>

    <!-- 1. TOP HUD BAR -->
    <header class="top-hud">
      <div class="brand-left">
        <div class="brand-icon">🎢</div>
        <div>
          <div class="brand-title">Belter Noun or Verb Skelter</div>
          <div class="brand-sub">Catch Each Word Crate Before It Rides Off the Belt!</div>
        </div>
      </div>

      <div class="hud-center">
        <button class="speed-btn" data-ms="10000" type="button">🐢 Slow 10s</button>
        <button class="speed-btn active" data-ms="7000" type="button">🚶 Normal 7s</button>
        <button class="speed-btn" data-ms="5000" type="button">🚀 Fast 5s</button>
        <div class="hud-sep"></div>
        <button class="action-chip" id="pauseBeltBtn" type="button">⏸️ Pause Belt</button>
        <button class="action-chip" id="swapWordBtn" type="button" title="Deal a different word to the same team">🔁 Swap Word</button>
        <button class="action-chip" id="finishRoundBtn" type="button">🏁 Podium</button>
      </div>

      <div class="hud-right">
        <button class="util-btn" id="repeatBtn" type="button"><span>🔊</span> Repeat</button>
        <button class="util-btn" id="setupBtn" type="button"><span>⚙️</span> Setup</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. TEAM SCOREBOARD & BINS -->
    <section class="teams-strip">
      <div class="team-cards-row" id="teamCardsRow" style="--team-count: 3"></div>

      <div class="round-progress-card">
        <div class="rp-top">
          <span id="roundLabelTxt">Round 1 &bull; Word 1 / 18</span>
          <span style="color:#D97706">⚡ Speed Bonus ON</span>
        </div>
        <div class="rp-bins">
          <div class="rp-bin-badge n">🧱 Nouns: <b id="nounBinCount">0</b></div>
          <div class="rp-bin-badge v">🏃 Verbs: <b id="verbBinCount">0</b></div>
        </div>
      </div>
    </section>

    <!-- 3. MAIN CONVEYOR MACHINE + TWO HELTER-SKELTER CHUTE BUTTONS -->
    <main class="conveyor-arena">
      <div class="machine-frame" id="machineFrame">
        <!-- Draining Clock Bar -->
        <div class="timer-bar-wrap">
          <div class="timer-bar-fill" id="timerBarFill"></div>
        </div>

        <!-- Turn & Sweet Spot Row -->
        <div class="machine-header-row">
          <div class="turn-banner-pill" id="turnBannerPill">
            <span>🦊</span> <span>Team Fox's Turn!</span>
          </div>
          <div style="display:flex;gap:8px">
            <button class="action-chip" id="ruleNounChip" type="button" style="background:#EFF6FF;color:#1E40AF;border-color:#93C5FD">
              🔊 What is a Noun?
            </button>
            <button class="action-chip" id="ruleVerbChip" type="button" style="background:#FFF7ED;color:#9A3412;border-color:#FDBA74">
              🔊 What is a Verb?
            </button>
          </div>
          <div class="sweet-spot-hint">
            🎯 Catch early for up to <b>+100 pts</b> + <b>🔥 Streak Bonus</b>!
          </div>
        </div>

        <!-- Worked Sentence Reveal Popup -->
        <div class="reveal-card" id="revealCard">
          <span class="rv-badge noun" id="revealBadge">🧱 NOUN!</span>
          <span class="rv-sentence" id="revealSentence">Look at the fluffy cat!</span>
        </div>

        <!-- Conveyor Belt Viewport -->
        <div class="belt-viewport" id="beltViewport">
          <div class="side-portal left-chute">
            <span style="font-size:26px">⚠️</span>
            <span>EDGE DROP</span>
          </div>

          <div class="sweet-spot-zone">
            <span>⭐ Sweet Spot Reading Zone ⭐</span>
          </div>

          <div class="side-portal right-entry">
            <span style="font-size:26px">📦</span>
            <span>CRATE IN</span>
          </div>

          <!-- Get-Ready Turn Card -->
          <div class="get-ready-card" id="getReadyCard" style="display:none">
            <div style="font-size:46px;line-height:1" id="grIcon">🦊</div>
            <div style="font-size:26px;font-weight:900;color:#1E293B;margin-top:4px" id="grTitle">Team Fox's Turn!</div>
            <div style="font-size:14px;font-weight:800;color:#D97706">Watch the belt and press NOUN or VERB!</div>
          </div>

          <!-- Moving Crate Rider -->
          <div class="crate-rider" id="crateRider">
            <div class="word-crate" id="wordCrate" title="Tap crate to hear word again!">
              <div class="crate-inner-frame"></div>
              <div class="crate-q-badge" id="crateQBadge">?</div>
              <div class="crate-pic" id="cratePic">🐱</div>
              <div class="crate-word-txt" id="crateWordTxt">cat</div>
            </div>
          </div>
        </div>

        <!-- Moving Conveyor Belt Treads & Spinning Rollers -->
        <div class="conveyor-deck">
          <div class="conveyor-treads" id="conveyorTreads"></div>
          <div class="roller-wheel">⚙️</div>
          <div class="roller-wheel">⚙️</div>
          <div class="roller-wheel">⚙️</div>
          <div class="roller-wheel">⚙️</div>
          <div class="roller-wheel">⚙️</div>
          <div class="roller-wheel">⚙️</div>
          <div class="roller-wheel">⚙️</div>
          <div class="roller-wheel">⚙️</div>
          <div class="roller-wheel">⚙️</div>
        </div>
      </div>

      <!-- TWO GIANT HELTER-SKELTER ANSWER BUTTONS -->
      <div class="answer-chutes-grid">
        <button class="ans-chute-btn noun" id="btnNoun" type="button">
          <span class="key-hint-pill">⬅️ Arrow / 1 / N</span>
          <div class="ac-slide-badge">🧱<span>🛝</span></div>
          <div class="ac-text">
            <div class="ac-title">NOUN</div>
            <div class="ac-desc">Person, Animal, Place, or Thing</div>
            <span class="ac-examples">🐱 cat &bull; 🍎 apple &bull; 🚌 bus &bull; 👶 baby</span>
          </div>
        </button>

        <button class="ans-chute-btn verb" id="btnVerb" type="button">
          <span class="key-hint-pill">➡️ Arrow / 2 / V</span>
          <div class="ac-text" style="text-align:right">
            <div class="ac-title">VERB</div>
            <div class="ac-desc">Action! Something You DO!</div>
            <span class="ac-examples">🏃 run &bull; 🤸 jump &bull; 🍽️ eat &bull; 👏 clap</span>
          </div>
          <div class="ac-slide-badge">🏃<span>🛝</span></div>
        </button>
      </div>
    </main>

    <!-- 4. QUICK-START / SETUP OVERLAY -->
    <div class="overlay-screen open" id="setupOverlay" role="dialog" aria-modal="true">
      <div class="dialog-card">
        <div style="font-size:58px;line-height:1">🎢📦</div>
        <h1 style="font-size:34px;font-weight:900;color:#1E3A8A;margin:6px 0 4px">Belter Noun or Verb Skelter!</h1>
        <p style="font-size:15px;font-weight:800;color:#475569;margin:0 0 12px">
          Easy Kindergarten words roll out on the conveyor belt! Press <b>🧱 NOUN</b> or <b>🏃 VERB</b> before the crate falls off the left edge!
        </p>

        <div class="setup-grid">
          <div class="setup-box">
            <div class="sb-title">👥 How Many Teams?</div>
            <div class="sb-opts" id="optTeams">
              <button type="button" class="sb-opt" data-teams="2">2 Teams</button>
              <button type="button" class="sb-opt active" data-teams="3">3 Teams</button>
              <button type="button" class="sb-opt" data-teams="4">4 Teams</button>
            </div>
          </div>

          <div class="setup-box">
            <div class="sb-title">⏱️ Conveyor Belt Speed</div>
            <div class="sb-opts" id="optSpeed">
              <button type="button" class="sb-opt" data-speedms="10000">🐢 10s</button>
              <button type="button" class="sb-opt active" data-speedms="7000">🚶 7s</button>
              <button type="button" class="sb-opt" data-speedms="5000">🚀 5s</button>
            </div>
          </div>

          <div class="setup-box">
            <div class="sb-title">📦 Crates per Round</div>
            <div class="sb-opts" id="optRoundLen">
              <button type="button" class="sb-opt" data-rlen="12">12</button>
              <button type="button" class="sb-opt active" data-rlen="18">18</button>
              <button type="button" class="sb-opt" data-rlen="24">24</button>
            </div>
          </div>
        </div>

        <button class="big-start-btn" id="startBeltBtn" type="button">
          <span>▶</span> <span>START THE BELT!</span>
        </button>
      </div>
    </div>

    <!-- 5. END-OF-ROUND PODIUM & REVIEW OVERLAY -->
    <div class="overlay-screen" id="podiumOverlay" role="dialog" aria-modal="true">
      <div class="dialog-card">
        <div style="font-size:56px;line-height:1">🏆</div>
        <h2 style="font-size:32px;font-weight:900;color:#92400E;margin:4px 0" id="podiumWinnerTitle">Team Fox Leads!</h2>
        <div style="font-size:14px;font-weight:800;color:#475569" id="podiumSubTitle">Round 1 Complete!</div>

        <div class="podium-list" id="podiumList"></div>

        <div id="missedSection" style="display:none;background:#FFF7ED;border:2.5px solid #FDBA74;border-radius:16px;padding:8px 12px;margin-bottom:12px">
          <div style="font-size:13px;font-weight:900;color:#9A3412">🔎 Worth Another Look (Tap any word to hear it!):</div>
          <div class="missed-chips" id="missedChips"></div>
        </div>

        <div style="display:flex;justify-content:center;gap:12px">
          <button class="big-start-btn" id="nextRoundBtn" type="button" style="font-size:20px;padding:10px 26px">
            <span>▶</span> <span>Next Round!</span>
          </button>
          <button class="big-start-btn" id="backToSetupBtn" type="button" style="font-size:18px;padding:10px 22px;background:#E2E8F0;border-color:#64748B;color:#1E293B;box-shadow:0 6px 0 #475569">
            <span>⚙️</span> <span>Settings</span>
          </button>
        </div>
      </div>
    </div>

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

<script src="assets/audio_belter_skelter.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. SIMPLIFIED KINDERGARTEN POOL (20 EASY NOUNS + 20 EASY VERBS) ============ */
  var EASY_WORDS = [
    // 20 Concrete Nouns
    { word: "cat",   type: "noun", emoji: "🐱", sentence: "Look at the fluffy cat!" },
    { word: "dog",   type: "noun", emoji: "🐶", sentence: "The happy dog wags its tail!" },
    { word: "pig",   type: "noun", emoji: "🐷", sentence: "The pink pig loves mud!" },
    { word: "cow",   type: "noun", emoji: "🐮", sentence: "The cow gives us fresh milk!" },
    { word: "hen",   type: "noun", emoji: "🐔", sentence: "The farm hen laid an egg!" },
    { word: "frog",  type: "noun", emoji: "🐸", sentence: "The green frog sits on a pad!" },
    { word: "lion",  type: "noun", emoji: "🦁", sentence: "The lion has a golden mane!" },
    { word: "bear",  type: "noun", emoji: "🐻", sentence: "The brown bear lives in the woods!" },
    { word: "apple", type: "noun", emoji: "🍎", sentence: "An apple is a crunchy red fruit!" },
    { word: "cake",  type: "noun", emoji: "🎂", sentence: "We eat sweet birthday cake!" },
    { word: "egg",   type: "noun", emoji: "🥚", sentence: "I eat a warm egg for breakfast!" },
    { word: "ball",  type: "noun", emoji: "⚽", sentence: "Bounce and kick the round ball!" },
    { word: "book",  type: "noun", emoji: "📖", sentence: "Open the book to read a story!" },
    { word: "hat",   type: "noun", emoji: "👒", sentence: "Put the sunny hat on your head!" },
    { word: "bed",   type: "noun", emoji: "🛏️", sentence: "Rest your head on the cozy bed!" },
    { word: "cup",   type: "noun", emoji: "🥤", sentence: "Pour cool water into the cup!" },
    { word: "bus",   type: "noun", emoji: "🚌", sentence: "The yellow bus goes to school!" },
    { word: "car",   type: "noun", emoji: "🚗", sentence: "The red car drives on the road!" },
    { word: "park",  type: "noun", emoji: "🏞️", sentence: "We play on slides at the park!" },
    { word: "baby",  type: "noun", emoji: "👶", sentence: "The little baby smiles and giggles!" },

    // 20 Action Verbs
    { word: "run",   type: "verb", emoji: "🏃", sentence: "I can run fast with my legs!" },
    { word: "jump",  type: "verb", emoji: "🤸", sentence: "I can jump high in the air!" },
    { word: "hop",   type: "verb", emoji: "🦘", sentence: "Hop on one foot like a bunny!" },
    { word: "walk",  type: "verb", emoji: "🚶", sentence: "Walk nicely down the path!" },
    { word: "swim",  type: "verb", emoji: "🏊", sentence: "Swim like a fish in the pool!" },
    { word: "fly",   type: "verb", emoji: "🦅", sentence: "Birds can fly high in the sky!" },
    { word: "sit",   type: "verb", emoji: "🪑", sentence: "Please sit down on the chair!" },
    { word: "climb", type: "verb", emoji: "🧗", sentence: "Climb up the tall green tree!" },
    { word: "eat",   type: "verb", emoji: "🍽️", sentence: "I eat my yummy lunch!" },
    { word: "drink", type: "verb", emoji: "🧃", sentence: "Drink cold orange juice!" },
    { word: "sleep", type: "verb", emoji: "😴", sentence: "Close your eyes and sleep!" },
    { word: "wash",  type: "verb", emoji: "🧼", sentence: "Wash your hands with bubbly soap!" },
    { word: "read",  type: "verb", emoji: "📚", sentence: "I read a funny storybook!" },
    { word: "draw",  type: "verb", emoji: "🖍️", sentence: "Draw a rainbow with crayons!" },
    { word: "clap",  type: "verb", emoji: "👏", sentence: "Clap your hands together!" },
    { word: "kick",  type: "verb", emoji: "🦵", sentence: "Kick the ball into the goal!" },
    { word: "sing",  type: "verb", emoji: "🎤", sentence: "Sing a happy song out loud!" },
    { word: "cry",   type: "verb", emoji: "😢", sentence: "Babies cry when they are hungry!" },
    { word: "hug",   type: "verb", emoji: "🤗", sentence: "Give your friend a big warm hug!" },
    { word: "dig",   type: "verb", emoji: "🪏", sentence: "Dig a hole in the soft sand!" }
  ];

  var TEAM_PALETTES = [
    { name: "Team Fox",   icon: "🦊", c: "#EA580C", bg: "#FFEDD5", bd: "#FB923C" },
    { name: "Team Panda", icon: "🐼", c: "#2563EB", bg: "#DBEAFE", bd: "#60A5FA" },
    { name: "Team Lion",  icon: "🦁", c: "#D97706", bg: "#FEF3C7", bd: "#FBBF24" },
    { name: "Team Frog",  icon: "🐸", c: "#059669", bg: "#D1FAE5", bd: "#34D399" }
  ];

  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. GAME STATE ============ */
  var state = {
    teamCount: 3,
    travelMs: 7000,
    roundLen: 18,
    roundNum: 1,
    teams: [],
    deck: [],
    turnIdx: 0,
    nounSorted: 0,
    verbSorted: 0,
    missedWords: [],
    armed: false,
    paused: false,
    rideStartedAt: 0,
    remainingMs: 7000,
    pausedAt: 0,
    turnTimeoutId: null,
    nextTurnTimerId: null,
    readyTimerId: null,
    soundOn: true,
    audioCtx: null,
    decoded: {},
    activeSources: [],
    activeHtmlAudio: null
  };

  function initTeams() {
    state.teams = [];
    for (var i = 0; i < state.teamCount; i++) {
      var p = TEAM_PALETTES[i];
      state.teams.push({
        idx: i,
        name: p.name,
        icon: p.icon,
        c: p.c,
        bg: p.bg,
        bd: p.bd,
        score: 0,
        right: 0,
        wrong: 0,
        streak: 0,
        bestStreak: 0
      });
    }
  }

  function buildBalancedDeck(count) {
    var nouns = shuffle(EASY_WORDS.filter(function(w) { return w.type === "noun"; }));
    var verbs = shuffle(EASY_WORDS.filter(function(w) { return w.type === "verb"; }));
    var combined = [];
    for (var i = 0; i < Math.ceil(count / 2); i++) {
      combined.push(nouns[i % nouns.length]);
      combined.push(verbs[i % verbs.length]);
    }
    return shuffle(combined.slice(0, count));
  }

  /* ============ 4. STUDIO VOICE ENGINE (US FEMALE JENNY + UK MALE RYAN) ============ */
  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 playSfx(type) {
    if (!state.soundOn) return;
    var ctx = ensureAudio();
    if (!ctx) return;
    try {
      var now = ctx.currentTime;
      if (type === "right") {
        [523.25, 659.25, 783.99, 1046.5].forEach(function(f, idx) {
          var o = ctx.createOscillator();
          var g = ctx.createGain();
          o.type = "triangle";
          o.frequency.setValueAtTime(f, now + idx * 0.06);
          g.gain.setValueAtTime(0.001, now + idx * 0.06);
          g.gain.exponentialRampToValueAtTime(0.11, now + idx * 0.06 + 0.015);
          g.gain.exponentialRampToValueAtTime(0.0001, now + idx * 0.06 + 0.22);
          o.connect(g); g.connect(ctx.destination);
          o.start(now + idx * 0.06);
          o.stop(now + idx * 0.06 + 0.24);
        });
      } else if (type === "wrong") {
        var o = ctx.createOscillator();
        var g = ctx.createGain();
        o.type = "sawtooth";
        o.frequency.setValueAtTime(220, now);
        o.frequency.exponentialRampToValueAtTime(115, now + 0.28);
        g.gain.setValueAtTime(0.001, now);
        g.gain.exponentialRampToValueAtTime(0.11, now + 0.02);
        g.gain.exponentialRampToValueAtTime(0.0001, now + 0.3);
        o.connect(g); g.connect(ctx.destination);
        o.start(now); o.stop(now + 0.32);
      } else {
        var o2 = ctx.createOscillator();
        var g2 = ctx.createGain();
        o2.type = "sine";
        o2.frequency.setValueAtTime(480, now);
        o2.frequency.exponentialRampToValueAtTime(680, now + 0.1);
        g2.gain.setValueAtTime(0.08, now);
        g2.gain.exponentialRampToValueAtTime(0.0001, now + 0.12);
        o2.connect(g2); g2.connect(ctx.destination);
        o2.start(now); o2.stop(now + 0.13);
      }
    } 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.BELTER_SKELTER_AUDIO && window.BELTER_SKELTER_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 playClip(key) {
    if (!state.soundOn) {
      await new Promise(function(r) { setTimeout(r, 900); });
      return;
    }
    var meta = window.BELTER_SKELTER_AUDIO && window.BELTER_SKELTER_AUDIO[key];
    if (!meta) return;
    stopAllSpeech();

    var ctx = ensureAudio();
    if (ctx) {
      try {
        var buf = await decodeClip(key);
        if (buf) {
          var span = buf._trimSpan || [0, buf.duration];
          var start = span[0];
          var dur = Math.max(0.08, span[1] - start);
          await new Promise(function(resolve) {
            var settled = false;
            function done() {
              if (settled) return;
              settled = true;
              resolve();
            }
            var src = ctx.createBufferSource();
            src.buffer = buf;
            src.connect(ctx.destination);
            src.onended = done;
            src.start(ctx.currentTime + 0.01, start, dur);
            state.activeSources.push(src);
            setTimeout(done, Math.ceil(dur * 1000) + 120);
          });
          return;
        }
      } catch (e) {}
    }

    await new Promise(function(resolve) {
      var a = new Audio(meta.src);
      state.activeHtmlAudio = a;
      a.onended = resolve;
      a.onerror = resolve;
      a.play().catch(resolve);
    });
  }

  function triggerConfetti() {
    var layer = document.getElementById("confettiLayer");
    if (!layer) return;
    var colors = ["#F59E0B", "#2563EB", "#EA580C", "#10B981", "#EC4899", "#FACC15"];
    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 = "50%";
      dot.style.top = "45%";
      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);
    }
  }

  /* ============ 5. RENDER TEAM SCOREBOARD & HUD ============ */
  function renderScoreboard() {
    var row = document.getElementById("teamCardsRow");
    if (!row) return;
    row.style.setProperty("--team-count", String(state.teams.length));

    var activeTeamIdx = state.turnIdx % state.teams.length;
    row.innerHTML = state.teams.map(function(t, idx) {
      var isTurn = idx === activeTeamIdx;
      return (
        '<div class="team-score-card' + (isTurn ? ' current-turn' : '') + '" id="teamCard_' + idx + '" style="border-color:' + (isTurn ? t.c : t.bd) + '">' +
          '<div class="tsc-left">' +
            '<div class="tsc-avatar" style="background:' + t.c + '">' + t.icon + '</div>' +
            '<div>' +
              '<div class="tsc-name">' + t.name + '</div>' +
              '<div class="tsc-sub">✅ ' + t.right + ' &bull; 🔥 Streak ' + t.streak + '</div>' +
            '</div>' +
          '</div>' +
          '<div class="tsc-right">' +
            (isTurn ? '<span class="tsc-turn-pill">UP NOW</span>' : '') +
            '<div class="tsc-pts" style="color:' + t.c + '">' + t.score + '</div>' +
          '</div>' +
        '</div>'
      );
    }).join("");

    document.getElementById("roundLabelTxt").textContent =
      "Round " + state.roundNum + " • Crate " + Math.min(state.turnIdx + 1, state.deck.length) + " / " + state.deck.length;
    document.getElementById("nounBinCount").textContent = String(state.nounSorted);
    document.getElementById("verbBinCount").textContent = String(state.verbSorted);
  }

  function clearTimers() {
    state.turnToken = (state.turnToken || 0) + 1;
    if (state.turnTimeoutId) { clearTimeout(state.turnTimeoutId); state.turnTimeoutId = null; }
    if (state.nextTurnTimerId) { clearTimeout(state.nextTurnTimerId); state.nextTurnTimerId = null; }
    if (state.readyTimerId) { clearTimeout(state.readyTimerId); state.readyTimerId = null; }
  }

  function setBeltMotionPaused(isPaused) {
    var rider = document.getElementById("crateRider");
    var treads = document.getElementById("conveyorTreads");
    var bar = document.getElementById("timerBarFill");
    if (rider) rider.classList.toggle("paused", isPaused);
    if (treads) treads.classList.toggle("paused", isPaused);
    if (bar) bar.classList.toggle("paused", isPaused);
    document.querySelectorAll(".roller-wheel").forEach(function(w) {
      w.classList.toggle("paused", isPaused);
    });
  }

  /* ============ 6. CORE CONVEYOR BELT TURN ENGINE ============ */
  function startNewMatch() {
    clearTimers();
    state.roundNum = 1;
    state.nounSorted = 0;
    state.verbSorted = 0;
    state.missedWords = [];
    initTeams();
    startRound();
  }

  function startRound() {
    clearTimers();
    state.deck = buildBalancedDeck(state.roundLen);
    state.turnIdx = 0;
    state.paused = false;
    document.getElementById("pauseBeltBtn").classList.remove("pause-active");
    document.getElementById("pauseBeltBtn").innerHTML = "⏸️ Pause Belt";
    document.getElementById("setupOverlay").classList.remove("open");
    document.getElementById("podiumOverlay").classList.remove("open");
    document.documentElement.style.setProperty("--ride-ms", state.travelMs + "ms");
    renderScoreboard();
    runTurn();
  }

  async function runTurn() {
    clearTimers();
    var myToken = state.turnToken;
    if (state.turnIdx >= state.deck.length) {
      showPodiumScreen();
      return;
    }

    var item = state.deck[state.turnIdx];
    var team = state.teams[state.turnIdx % state.teams.length];
    state.armed = false;

    renderScoreboard();

    // Update Turn Banner
    var turnPill = document.getElementById("turnBannerPill");
    turnPill.innerHTML = '<span>' + team.icon + '</span> <span>' + team.name + '’s Turn!</span>';
    turnPill.style.borderColor = team.bd;

    // Hide previous reveal & stamps
    var revealCard = document.getElementById("revealCard");
    revealCard.classList.remove("show");

    var crate = document.getElementById("wordCrate");
    crate.className = "word-crate";
    crate.querySelectorAll(".verdict-stamp, .floating-score").forEach(function(el) { el.remove(); });
    document.getElementById("cratePic").textContent = item.emoji;
    document.getElementById("crateWordTxt").textContent = item.word;
    document.getElementById("crateQBadge").textContent = "?";

    var btnNoun = document.getElementById("btnNoun");
    var btnVerb = document.getElementById("btnVerb");
    btnNoun.className = "ans-chute-btn noun";
    btnVerb.className = "ans-chute-btn verb";
    btnNoun.disabled = true;
    btnVerb.disabled = true;

    // Reset rider position off-screen while showing quick "Get Ready!" card
    var rider = document.getElementById("crateRider");
    rider.className = "crate-rider";
    rider.style.display = "none";

    var bar = document.getElementById("timerBarFill");
    bar.className = "timer-bar-fill";

    var grCard = document.getElementById("getReadyCard");
    document.getElementById("grIcon").textContent = team.icon;
    document.getElementById("grTitle").textContent = team.name + " — Get Ready!";
    grCard.style.display = "block";

    await playClip("turn_" + team.idx);
    if (myToken !== state.turnToken) return;

    state.readyTimerId = setTimeout(function() {
      if (myToken !== state.turnToken) return;
      grCard.style.display = "none";
      launchCrateOnBelt(item);
    }, 220);
  }

  function launchCrateOnBelt(item) {
    var rider = document.getElementById("crateRider");
    var bar = document.getElementById("timerBarFill");
    var btnNoun = document.getElementById("btnNoun");
    var btnVerb = document.getElementById("btnVerb");

    document.documentElement.style.setProperty("--ride-ms", state.travelMs + "ms");

    rider.style.display = "block";
    rider.className = "crate-rider";
    void rider.offsetWidth; // Reflow to restart animation cleanly
    rider.className = "crate-rider riding";

    bar.className = "timer-bar-fill";
    void bar.offsetWidth;
    bar.className = "timer-bar-fill draining";

    setBeltMotionPaused(false);

    btnNoun.disabled = false;
    btnVerb.disabled = false;
    state.armed = true;
    state.paused = false;
    state.rideStartedAt = performance.now();
    state.remainingMs = state.travelMs;

    // Speak the word immediately as the crate rolls onto the belt!
    playSfx("pop");
    playClip("say_" + item.word);

    state.turnTimeoutId = setTimeout(function() {
      resolveTurn(null);
    }, state.travelMs);
  }

  async function resolveTurn(pressedType) {
    if (!state.armed) return;
    state.armed = false;
    clearTimers();
    var myToken = state.turnToken;

    var elapsed = performance.now() - state.rideStartedAt;
    var limit = state.travelMs;

    // Freeze the conveyor belt and crate right where it was caught!
    setBeltMotionPaused(true);

    var item = state.deck[state.turnIdx];
    var team = state.teams[state.turnIdx % state.teams.length];
    var timedOut = (pressedType === null);
    var correct = !timedOut && (pressedType === item.type);

    var btnNoun = document.getElementById("btnNoun");
    var btnVerb = document.getElementById("btnVerb");
    btnNoun.disabled = true;
    btnVerb.disabled = true;

    if (pressedType === "noun") btnNoun.classList.add(correct ? "hit-right" : "hit-wrong");
    if (pressedType === "verb") btnVerb.classList.add(correct ? "hit-right" : "hit-wrong");

    var pts = 0;
    var streakBonus = 0;
    if (correct) {
      var ratioLeft = Math.max(0, Math.min(1, 1 - (elapsed / limit)));
      pts = Math.round((40 + 60 * ratioLeft) / 5) * 5;
      team.streak += 1;
      if (team.streak > team.bestStreak) team.bestStreak = team.streak;
      if (team.streak >= 2) streakBonus = 15;
      if (team.streak >= 4) streakBonus = 25;
      team.right += 1;
      team.score += (pts + streakBonus);
      if (item.type === "noun") state.nounSorted += 1;
      else state.verbSorted += 1;
    } else {
      team.streak = 0;
      team.wrong += 1;
      state.missedWords.push(item);
    }

    // Stamp the crate & show floating points
    var crate = document.getElementById("wordCrate");
    crate.classList.add(correct ? ("right-" + item.type) : "wrong-hit");

    var stamp = document.createElement("div");
    stamp.className = "verdict-stamp";
    var stampTxt = timedOut ? "⏰ MISSED!" : (correct ? ("✓ " + item.type.toUpperCase() + "!") : "✕ OOPS!");
    stamp.innerHTML = '<div class="stamp-pill ' + (correct ? 'ok' : 'no') + '">' + stampTxt + '</div>';
    crate.appendChild(stamp);

    var floatEl = document.createElement("div");
    floatEl.className = "floating-score" + (correct ? "" : " zero");
    floatEl.textContent = correct
      ? ("+" + (pts + streakBonus) + (streakBonus ? " 🔥" : ""))
      : "+0";
    crate.appendChild(floatEl);

    if (correct) {
      playSfx("right");
      triggerConfetti();
      var cardEl = document.getElementById("teamCard_" + team.idx);
      if (cardEl) cardEl.classList.add("bump");
    } else {
      playSfx("wrong");
      var mf = document.getElementById("machineFrame");
      mf.classList.add("shake");
      setTimeout(function() { mf.classList.remove("shake"); }, 480);
    }

    // Show Worked Sentence Reveal Card & Speak British Male Ryan's Reveal!
    var revealCard = document.getElementById("revealCard");
    var revealBadge = document.getElementById("revealBadge");
    var revealSentence = document.getElementById("revealSentence");
    revealBadge.className = "rv-badge " + item.type;
    revealBadge.textContent = item.type === "noun" ? "🧱 IT'S A NOUN!" : "🏃 IT'S A VERB!";
    revealSentence.textContent = item.emoji + " " + item.sentence;
    revealCard.classList.add("show");

    renderScoreboard();

    // Wait for British Male Ryan to completely finish reading the answer & sentence!
    if (timedOut) {
      await playClip("time_up");
      if (myToken !== state.turnToken) return;
      await playClip("ans_" + item.word);
    } else {
      await playClip("ans_" + item.word);
    }
    if (myToken !== state.turnToken) return;

    // Give a comfortable 750ms breathing pause AFTER Ryan finishes speaking before advancing!
    state.nextTurnTimerId = setTimeout(function() {
      if (myToken !== state.turnToken) return;
      state.turnIdx += 1;
      runTurn();
    }, 750);
  }

  /* ============ 7. PAUSE / RESUME & SWAP WORD ============ */
  function togglePauseBelt() {
    var pauseBtn = document.getElementById("pauseBeltBtn");
    if (!state.armed) return;
    if (!state.paused) {
      state.paused = true;
      clearTimeout(state.turnTimeoutId);
      var spent = performance.now() - state.rideStartedAt;
      state.remainingMs = Math.max(450, state.remainingMs - spent);
      setBeltMotionPaused(true);
      pauseBtn.classList.add("pause-active");
      pauseBtn.innerHTML = "▶ Resume Belt";
    } else {
      state.paused = false;
      state.rideStartedAt = performance.now();
      setBeltMotionPaused(false);
      pauseBtn.classList.remove("pause-active");
      pauseBtn.innerHTML = "⏸️ Pause Belt";
      state.turnTimeoutId = setTimeout(function() {
        resolveTurn(null);
      }, state.remainingMs);
    }
  }

  function swapCurrentWord() {
    clearTimers();
    var cur = state.deck[state.turnIdx];
    var pool = EASY_WORDS.filter(function(w) { return !cur || w.word !== cur.word; });
    state.deck[state.turnIdx] = pool[Math.floor(Math.random() * pool.length)];
    runTurn();
  }

  /* ============ 8. END-OF-ROUND PODIUM & REVIEW SCREEN ============ */
  function showPodiumScreen() {
    clearTimers();
    setBeltMotionPaused(true);
    var ranked = state.teams.slice().sort(function(a, b) { return b.score - a.score; });
    var medals = ["🥇", "🥈", "🥉", "🏅"];

    document.getElementById("podiumWinnerTitle").textContent =
      ranked[0].icon + " " + ranked[0].name + " Wins Round " + state.roundNum + "!";
    document.getElementById("podiumSubTitle").textContent =
      "🧱 " + state.nounSorted + " Nouns & 🏃 " + state.verbSorted + " Verbs Sorted!";

    var listEl = document.getElementById("podiumList");
    listEl.innerHTML = ranked.map(function(t, i) {
      return (
        '<div class="podium-row" style="border-color:' + t.bd + '">' +
          '<div style="display:flex;align-items:center;gap:10px">' +
            '<span style="font-size:28px">' + medals[i] + '</span>' +
            '<span style="font-size:26px">' + t.icon + '</span>' +
            '<div>' +
              '<div style="font-size:17px;font-weight:900;color:#0F172A">' + t.name + '</div>' +
              '<div style="font-size:12px;font-weight:800;color:#475569">✅ ' + t.right + ' right &bull; Best streak 🔥 ' + t.bestStreak + '</div>' +
            '</div>' +
          '</div>' +
          '<div style="font-size:26px;font-weight:900;color:' + t.c + '">' + t.score + ' pts</div>' +
        '</div>'
      );
    }).join("");

    // Missed words review chips
    var missedSec = document.getElementById("missedSection");
    var missedChips = document.getElementById("missedChips");
    if (state.missedWords.length > 0) {
      var seen = {};
      var uniq = state.missedWords.filter(function(m) {
        if (seen[m.word]) return false;
        seen[m.word] = true;
        return true;
      });
      missedSec.style.display = "block";
      missedChips.innerHTML = uniq.map(function(m) {
        return (
          '<button type="button" class="missed-chip" data-review="' + m.word + '">' +
            '<span>' + m.emoji + '</span> <span>' + m.word + '</span> ' +
            '<span style="color:' + (m.type === "noun" ? "#2563EB" : "#EA580C") + '">(' + m.type.toUpperCase() + ')</span>' +
          '</button>'
        );
      }).join("");

      missedChips.querySelectorAll("[data-review]").forEach(function(btn) {
        btn.addEventListener("click", function() {
          playClip("ans_" + btn.getAttribute("data-review"));
        });
      });
    } else {
      missedSec.style.display = "none";
    }

    document.getElementById("podiumOverlay").classList.add("open");
    triggerConfetti();
    playClip("winner");
  }

  /* ============ 9. WIRE BUTTONS & KEYBOARD SHORTCUTS ============ */
  document.getElementById("btnNoun").addEventListener("click", function() {
    resolveTurn("noun");
  });
  document.getElementById("btnVerb").addEventListener("click", function() {
    resolveTurn("verb");
  });

  document.getElementById("wordCrate").addEventListener("click", function() {
    var item = state.deck[state.turnIdx];
    if (item) playClip("say_" + item.word);
  });

  document.getElementById("repeatBtn").addEventListener("click", function() {
    var item = state.deck[state.turnIdx];
    if (item) playClip(state.armed ? ("say_" + item.word) : ("ans_" + item.word));
  });

  document.getElementById("pauseBeltBtn").addEventListener("click", togglePauseBelt);
  document.getElementById("swapWordBtn").addEventListener("click", swapCurrentWord);
  document.getElementById("finishRoundBtn").addEventListener("click", showPodiumScreen);

  document.getElementById("ruleNounChip").addEventListener("click", function() {
    playClip("rule_noun");
  });
  document.getElementById("ruleVerbChip").addEventListener("click", function() {
    playClip("rule_verb");
  });

  // Top HUD Speed Switcher
  document.querySelectorAll(".speed-btn").forEach(function(btn) {
    btn.addEventListener("click", function() {
      state.travelMs = parseInt(btn.getAttribute("data-ms"), 10);
      document.documentElement.style.setProperty("--ride-ms", state.travelMs + "ms");
      document.querySelectorAll(".speed-btn").forEach(function(b) {
        b.classList.toggle("active", b === btn);
      });
    });
  });

  // Setup Dialog Options
  document.querySelectorAll("[data-teams]").forEach(function(btn) {
    btn.addEventListener("click", function() {
      state.teamCount = parseInt(btn.getAttribute("data-teams"), 10);
      document.querySelectorAll("[data-teams]").forEach(function(b) {
        b.classList.toggle("active", b === btn);
      });
    });
  });

  document.querySelectorAll("[data-speedms]").forEach(function(btn) {
    btn.addEventListener("click", function() {
      state.travelMs = parseInt(btn.getAttribute("data-speedms"), 10);
      document.querySelectorAll("[data-speedms]").forEach(function(b) {
        b.classList.toggle("active", b === btn);
      });
      document.querySelectorAll(".speed-btn").forEach(function(b) {
        b.classList.toggle("active", parseInt(b.getAttribute("data-ms"), 10) === state.travelMs);
      });
    });
  });

  document.querySelectorAll("[data-rlen]").forEach(function(btn) {
    btn.addEventListener("click", function() {
      state.roundLen = parseInt(btn.getAttribute("data-rlen"), 10);
      document.querySelectorAll("[data-rlen]").forEach(function(b) {
        b.classList.toggle("active", b === btn);
      });
    });
  });

  document.getElementById("startBeltBtn").addEventListener("click", function() {
    ensureAudio();
    startNewMatch();
  });

  document.getElementById("nextRoundBtn").addEventListener("click", function() {
    state.roundNum += 1;
    state.missedWords = [];
    startRound();
  });

  document.getElementById("setupBtn").addEventListener("click", function() {
    clearTimers();
    setBeltMotionPaused(true);
    document.getElementById("setupOverlay").classList.add("open");
  });

  document.getElementById("backToSetupBtn").addEventListener("click", function() {
    document.getElementById("podiumOverlay").classList.remove("open");
    document.getElementById("setupOverlay").classList.add("open");
  });

  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() {});
    }
  });

  window.addEventListener("keydown", function(e) {
    var k = e.key.toLowerCase();
    if (k === "arrowleft" || k === "1" || k === "n") {
      e.preventDefault();
      resolveTurn("noun");
    } else if (k === "arrowright" || k === "2" || k === "v") {
      e.preventDefault();
      resolveTurn("verb");
    } else if (k === "p") {
      e.preventDefault();
      togglePauseBelt();
    }
  });

  // Initialize initial preview state behind Setup Dialog
  initTeams();
  state.deck = buildBalancedDeck(state.roundLen);
  renderScoreboard();
})();
</script>
</body>
</html>