套件原始碼 · Am, Is, Are All-Stars

CC BY 4.0 · 請標明 @joe · v1

← 遊戲詳情

拿走、改作、拿來教

下方程式碼只是套件可遊玩的入口 HTML。請下載完整 ZIP,才能連同所有相對素材一起編輯或改作。

⬇ 下載入口 HTML ⬇ 下載完整套件
AI 編輯提示起頭
Edit this classroom game for interactive whiteboards. Keep large touch targets and whole-class play. Tracking, logins, personal-data collection, and remote API calls are prohibited. Use the downloaded complete ZIP—not only the entry HTML shown below—and preserve its browser-ready folder structure. Preserve the educational goal. Original creator: @joe on ClassBoard Games (CC BY 4.0).
僅入口 HTML · index.html · 4555.2 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>am, is, are — Verb to Be Kingdom &amp; Cup Castle Race</title>
<style>
  :root {
    --app-scale: 1;
    --ink: #1E293B;
    --am-c: #EC4899;
    --am-bg: #FDF2F8;
    --am-bd: #F472B6;
    --is-c: #2563EB;
    --is-bg: #EFF6FF;
    --is-bd: #60A5FA;
    --are-c: #059669;
    --are-bg: #ECFDF5;
    --are-bd: #34D399;
    --gold: #F59E0B;
  }

  * {
    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 20% 15%, #38BDF8 0%, #2563EB 48%, #1E1B4B 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, #F0F9FF 0%, #ECFDF5 55%, #FEFCE8 100%);
    border-radius: 30px;
    border: 6px solid #FBBF24;
    box-shadow: 0 24px 65px rgba(15, 23, 42, 0.55), inset 0 2px 0 rgba(255,255,255,0.9);
    display: flex;
    flex-direction: column;
    padding: 12px 16px;
    gap: 9px;
    position: relative;
    overflow: hidden;
  }

  /* Soft decorative background dots */
  .stage-bg-pattern {
    position: absolute;
    inset: 0;
    pointer-events: none;
    background-image: radial-gradient(rgba(59, 130, 246, 0.12) 2px, transparent 2px);
    background-size: 32px 32px;
    z-index: 0;
  }

  /* ============ 1. TOP HEADER BAR ============ */
  .top-bar {
    position: relative;
    z-index: 5;
    height: 60px;
    background: linear-gradient(90deg, #1E3A8A 0%, #2563EB 50%, #0D9488 100%);
    border-radius: 20px;
    border: 3px solid #93C5FD;
    box-shadow: 0 6px 0 rgba(30, 58, 138, 0.35);
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 0 14px;
    color: #fff;
    flex-shrink: 0;
  }

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

  .brand-logo {
    width: 44px;
    height: 44px;
    border-radius: 14px;
    background: linear-gradient(135deg, #FDE047, #F97316);
    border: 3px solid #FFFBEB;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 25px;
    box-shadow: 0 4px 0 rgba(124, 45, 18, 0.35);
  }

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

  .brand-sub {
    font-size: 11.5px;
    font-weight: 800;
    color: #BAE6FD;
    letter-spacing: 0.3px;
  }

  /* Mode Switch Tabs */
  .mode-tabs {
    display: flex;
    align-items: center;
    gap: 6px;
    background: rgba(15, 23, 42, 0.28);
    padding: 4px;
    border-radius: 14px;
    border: 2px solid rgba(255, 255, 255, 0.25);
  }

  .mode-tab {
    border: none;
    background: transparent;
    color: #E0F2FE;
    font-family: inherit;
    font-size: 13.5px;
    font-weight: 900;
    padding: 7px 13px;
    border-radius: 10px;
    cursor: pointer;
    display: flex;
    align-items: center;
    gap: 6px;
    transition: all 0.16s ease;
  }

  .mode-tab:hover {
    background: rgba(255, 255, 255, 0.14);
    color: #fff;
  }

  .mode-tab.active {
    background: linear-gradient(180deg, #FDE047, #F59E0B);
    color: #451A03;
    box-shadow: 0 3px 0 #B45309;
    transform: translateY(-1px);
  }

  /* Right utility controls (strictly 4 clean buttons + star counter) */
  .top-right {
    display: flex;
    align-items: center;
    gap: 7px;
  }

  .star-pill {
    background: rgba(255, 255, 255, 0.18);
    border: 2px solid #FDE047;
    border-radius: 999px;
    padding: 5px 11px;
    font-size: 14px;
    font-weight: 900;
    color: #FEF08A;
    display: flex;
    align-items: center;
    gap: 5px;
  }

  .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;
    transition: transform 0.12s, background 0.15s;
  }

  .util-btn:hover {
    background: rgba(255, 255, 255, 0.28);
    transform: translateY(-1px);
  }

  .util-btn:active {
    transform: translateY(1px);
  }

  /* ============ 2. PRONOUN GRAMMAR HELPER & VOICE BAR ============ */
  .grammar-bar {
    position: relative;
    z-index: 5;
    height: 58px;
    background: #FFFFFF;
    border-radius: 18px;
    border: 3px solid #BFDBFE;
    box-shadow: 0 5px 0 rgba(59, 130, 246, 0.12);
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 0 12px;
    gap: 10px;
    flex-shrink: 0;
  }

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

  .rule-pill {
    border: 3px solid transparent;
    border-radius: 14px;
    padding: 5px 11px;
    cursor: pointer;
    display: flex;
    align-items: center;
    gap: 7px;
    font-family: inherit;
    transition: transform 0.14s, box-shadow 0.14s;
  }

  .rule-pill:hover {
    transform: translateY(-2px);
  }

  .rule-pill.am {
    background: var(--am-bg);
    border-color: var(--am-bd);
    color: #9D174D;
  }
  .rule-pill.is {
    background: var(--is-bg);
    border-color: var(--is-bd);
    color: #1E40AF;
  }
  .rule-pill.are {
    background: var(--are-bg);
    border-color: var(--are-bd);
    color: #065F46;
  }

  .rule-pill.highlight {
    transform: scale(1.05);
    box-shadow: 0 0 0 4px rgba(251, 191, 36, 0.55);
  }

  .rp-verb {
    font-size: 17px;
    font-weight: 900;
    padding: 2px 8px;
    border-radius: 9px;
    color: #fff;
    text-transform: lowercase;
  }
  .rule-pill.am .rp-verb { background: var(--am-c); }
  .rule-pill.is .rp-verb { background: var(--is-c); }
  .rule-pill.are .rp-verb { background: var(--are-c); }

  .rp-subj {
    font-size: 12.5px;
    font-weight: 900;
    line-height: 1.15;
    text-align: left;
  }
  .rp-subj small {
    display: block;
    font-size: 10.5px;
    opacity: 0.85;
  }

  .voice-actions {
    display: flex;
    align-items: center;
    gap: 7px;
  }

  .act-btn {
    border: 3px solid #F59E0B;
    background: linear-gradient(180deg, #FEF08A, #FBBF24);
    color: #78350F;
    font-family: inherit;
    font-size: 13px;
    font-weight: 900;
    padding: 6px 12px;
    border-radius: 13px;
    box-shadow: 0 4px 0 #B45309;
    cursor: pointer;
    display: flex;
    align-items: center;
    gap: 5px;
    transition: transform 0.12s;
  }

  .act-btn:hover { transform: translateY(-1px); }
  .act-btn:active { transform: translateY(2px); box-shadow: 0 1px 0 #B45309; }

  .act-btn.uk {
    border-color: #10B981;
    background: linear-gradient(180deg, #A7F3D0, #34D399);
    color: #064E3B;
    box-shadow: 0 4px 0 #047857;
  }

  .act-btn.next {
    border-color: #6366F1;
    background: linear-gradient(180deg, #C7D2FE, #818CF8);
    color: #1E1B4B;
    box-shadow: 0 4px 0 #4338CA;
  }

  /* ============ 3. MAIN SPLIT: LEFT GRAMMAR STAGE + RIGHT CUP CASTLE RACE ============ */
  .main-split {
    position: relative;
    z-index: 5;
    flex: 1;
    min-height: 0;
    display: grid;
    grid-template-columns: 1fr 375px;
    gap: 12px;
  }

  .grammar-stage {
    background: rgba(255, 255, 255, 0.92);
    border-radius: 24px;
    border: 4px solid #93C5FD;
    box-shadow: 0 10px 25px rgba(30, 58, 138, 0.12);
    padding: 12px 16px;
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    position: relative;
    overflow: hidden;
  }

  /* World Theme Filter Pills */
  .theme-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    flex-shrink: 0;
  }

  .theme-chips {
    display: flex;
    align-items: center;
    gap: 6px;
    flex-wrap: wrap;
  }

  .theme-chip {
    border: 2px solid #CBD5E1;
    background: #F8FAFC;
    color: #475569;
    font-family: inherit;
    font-size: 12px;
    font-weight: 800;
    padding: 4px 10px;
    border-radius: 999px;
    cursor: pointer;
    transition: all 0.14s;
  }

  .theme-chip.active {
    background: #2563EB;
    border-color: #1D4ED8;
    color: #fff;
    box-shadow: 0 2px 0 #1E3A8A;
  }

  .q-counter-badge {
    font-size: 12.5px;
    font-weight: 900;
    color: #1E40AF;
    background: #DBEAFE;
    border: 2px solid #93C5FD;
    padding: 4px 11px;
    border-radius: 999px;
  }

  /* Illustrated Scene & Subject Detective Card */
  .scene-card {
    background: linear-gradient(180deg, #F8FAFC 0%, #EFF6FF 100%);
    border: 3px solid #BFDBFE;
    border-radius: 22px;
    padding: 14px 18px;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 10px;
    position: relative;
    flex: 1;
    margin: 8px 0;
    box-shadow: inset 0 2px 6px rgba(255,255,255,0.9);
  }

  .detective-row {
    display: flex;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
    justify-content: center;
  }

  .subj-badge {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    padding: 5px 14px;
    border-radius: 999px;
    font-size: 14px;
    font-weight: 900;
    border: 2.5px solid;
    box-shadow: 0 3px 0 rgba(0,0,0,0.08);
  }
  .subj-badge.am { background: #FDF2F8; border-color: #F472B6; color: #9D174D; }
  .subj-badge.is { background: #EFF6FF; border-color: #60A5FA; color: #1E40AF; }
  .subj-badge.are { background: #ECFDF5; border-color: #34D399; color: #065F46; }

  .scene-illustration {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 14px;
  }

  .big-emoji-bubble {
    width: 86px;
    height: 86px;
    border-radius: 24px;
    background: #FFFFFF;
    border: 4px solid #60A5FA;
    box-shadow: 0 7px 0 rgba(37, 99, 235, 0.2);
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 50px;
    animation: gentleBob 2.8s ease-in-out infinite;
  }

  @keyframes gentleBob {
    0%, 100% { transform: translateY(0) rotate(0deg); }
    50% { transform: translateY(-5px) rotate(2deg); }
  }

  /* Sentence Display Box */
  .sentence-bridge-box {
    background: #FFFFFF;
    border: 4px solid #60A5FA;
    border-radius: 22px;
    padding: 16px 20px;
    width: 100%;
    text-align: center;
    box-shadow: 0 8px 20px rgba(37, 99, 235, 0.12);
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: center;
    gap: 10px;
    font-size: 31px;
    font-weight: 900;
    color: #0F172A;
    line-height: 1.3;
  }

  .subj-highlight {
    background: #FEF9C3;
    border: 2px dashed #F59E0B;
    padding: 2px 10px;
    border-radius: 12px;
    color: #92400E;
  }

  .verb-slot {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 112px;
    height: 54px;
    padding: 0 16px;
    border-radius: 16px;
    border: 4px dashed #94A3B8;
    background: #F1F5F9;
    color: #64748B;
    font-size: 32px;
    font-weight: 900;
    transition: all 0.25s cubic-bezier(0.34, 1.56, 0.64, 1);
  }

  .verb-slot.filled {
    border-style: solid;
    transform: scale(1.08);
    color: #FFFFFF;
    box-shadow: 0 6px 0 rgba(0,0,0,0.2);
  }
  .verb-slot.filled.am { background: linear-gradient(180deg, #F472B6, #DB2777); border-color: #9D174D; }
  .verb-slot.filled.is { background: linear-gradient(180deg, #60A5FA, #2563EB); border-color: #1E3A8A; }
  .verb-slot.filled.are { background: linear-gradient(180deg, #34D399, #059669); border-color: #064E3B; }

  /* Answer Choice Buttons (am, is, are) */
  .choices-row {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 14px;
    width: 100%;
    flex-shrink: 0;
  }

  .verb-choice-btn {
    height: 88px;
    border-radius: 22px;
    border: 4px solid;
    font-family: inherit;
    cursor: pointer;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    transition: transform 0.14s cubic-bezier(0.34, 1.56, 0.64, 1), box-shadow 0.14s, opacity 0.15s;
    position: relative;
  }

  .verb-choice-btn.am {
    background: linear-gradient(180deg, #FDF2F8 0%, #FCE7F3 100%);
    border-color: #EC4899;
    color: #9D174D;
    box-shadow: 0 7px 0 #BE185D;
  }

  .verb-choice-btn.is {
    background: linear-gradient(180deg, #EFF6FF 0%, #DBEAFE 100%);
    border-color: #2563EB;
    color: #1E40AF;
    box-shadow: 0 7px 0 #1D4ED8;
  }

  .verb-choice-btn.are {
    background: linear-gradient(180deg, #ECFDF5 0%, #D1FAE5 100%);
    border-color: #059669;
    color: #065F46;
    box-shadow: 0 7px 0 #047857;
  }

  .verb-choice-btn:hover:not(:disabled) {
    transform: translateY(-3px);
  }

  .verb-choice-btn:active:not(:disabled) {
    transform: translateY(4px);
    box-shadow: 0 2px 0 rgba(0,0,0,0.25);
  }

  .verb-choice-btn.wrong {
    opacity: 0.42;
    transform: scale(0.95);
    filter: grayscale(0.5);
    cursor: not-allowed;
  }

  .verb-choice-btn.correct {
    background: linear-gradient(180deg, #4ADE80, #16A34A) !important;
    border-color: #14532D !important;
    color: #FFFFFF !important;
    box-shadow: 0 7px 0 #14532D !important;
    transform: scale(1.04);
  }

  .vc-word {
    font-size: 36px;
    font-weight: 900;
    line-height: 1;
  }

  .vc-hint {
    font-size: 12px;
    font-weight: 800;
    margin-top: 4px;
    opacity: 0.88;
  }

  /* ============ MODE 2: VERB BASKET SORT ============ */
  .sort-stage-wrap {
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    height: 100%;
    gap: 10px;
  }

  .mystery-bubble-card {
    flex: 1;
    background: radial-gradient(circle at 50% 35%, #FFFFFF 0%, #EFF6FF 100%);
    border: 4px solid #60A5FA;
    border-radius: 24px;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    padding: 14px;
    gap: 8px;
    position: relative;
    box-shadow: 0 8px 22px rgba(37, 99, 235, 0.14);
  }

  .subject-float-pill {
    background: linear-gradient(135deg, #FEF9C3, #FDE047);
    border: 4px solid #F59E0B;
    border-radius: 26px;
    padding: 12px 26px;
    display: flex;
    align-items: center;
    gap: 14px;
    box-shadow: 0 8px 0 #D97706;
    animation: gentleBob 2.5s ease-in-out infinite;
  }

  .sf-emoji {
    font-size: 48px;
    line-height: 1;
  }

  .sf-text {
    font-size: 32px;
    font-weight: 900;
    color: #78350F;
  }

  .baskets-row {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 14px;
  }

  .basket-btn {
    height: 126px;
    border-radius: 24px;
    border: 4px solid;
    font-family: inherit;
    cursor: pointer;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    padding: 10px;
    transition: transform 0.15s cubic-bezier(0.34, 1.56, 0.64, 1);
    position: relative;
  }

  .basket-btn:hover:not(:disabled) {
    transform: translateY(-4px) scale(1.02);
  }

  .basket-btn.am {
    background: linear-gradient(180deg, #FCE7F3 0%, #FBCFE8 100%);
    border-color: #DB2777;
    color: #831843;
    box-shadow: 0 8px 0 #9D174D;
  }
  .basket-btn.is {
    background: linear-gradient(180deg, #DBEAFE 0%, #BFDBFE 100%);
    border-color: #2563EB;
    color: #1E3A8A;
    box-shadow: 0 8px 0 #1E40AF;
  }
  .basket-btn.are {
    background: linear-gradient(180deg, #D1FAE5 0%, #A7F3D0 100%);
    border-color: #059669;
    color: #064E3B;
    box-shadow: 0 8px 0 #065F46;
  }

  .basket-icon {
    font-size: 26px;
    line-height: 1;
  }
  .basket-title {
    font-size: 30px;
    font-weight: 900;
    margin: 2px 0;
  }
  .basket-sub {
    font-size: 12px;
    font-weight: 800;
    background: rgba(255,255,255,0.75);
    padding: 2px 10px;
    border-radius: 999px;
  }

  /* ============ 4. RIGHT SIDE: 3-TEAM CUP CASTLE TOWER RACE ============ */
  .race-stage {
    background: linear-gradient(180deg, #87CEEB 0%, #E0F2FE 78%, #86EFAC 78%, #22C55E 100%);
    border-radius: 24px;
    border: 4px solid #60A5FA;
    box-shadow: 0 10px 25px rgba(30, 58, 138, 0.15);
    padding: 10px 12px;
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    position: relative;
    overflow: hidden;
  }

  .rs-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    background: rgba(255, 255, 255, 0.92);
    padding: 6px 10px;
    border-radius: 14px;
    border: 2px solid #93C5FD;
    z-index: 3;
  }

  .rs-title {
    font-size: 15px;
    font-weight: 900;
    color: #1E3A8A;
    display: flex;
    align-items: center;
    gap: 5px;
  }

  .towers-grid {
    flex: 1;
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 8px;
    padding: 8px 0 4px;
    position: relative;
    z-index: 2;
    min-height: 0;
  }

  .team-column {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: space-between;
    height: 100%;
    background: rgba(255, 255, 255, 0.35);
    border-radius: 16px;
    border: 2px solid rgba(255, 255, 255, 0.65);
    padding: 6px 4px;
    cursor: pointer;
    transition: border-color 0.15s, background 0.15s;
  }

  .team-column.active-turn {
    background: rgba(255, 255, 255, 0.68);
    border: 3px solid #F59E0B;
    box-shadow: 0 0 0 3px rgba(251, 191, 36, 0.45);
  }

  .team-top-pill {
    width: 100%;
    background: rgba(255, 255, 255, 0.94);
    border-radius: 11px;
    padding: 4px 5px;
    text-align: center;
    border: 2px solid;
    flex-shrink: 0;
  }

  .tt-name {
    font-size: 12px;
    font-weight: 900;
    line-height: 1.1;
  }

  .tt-score-row {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 5px;
    margin-top: 2px;
    font-size: 13px;
    font-weight: 900;
    color: #1E293B;
  }

  .minus-cup-btn {
    width: 19px;
    height: 19px;
    border-radius: 50%;
    border: 1.5px solid #FCA5A5;
    background: #FEE2E2;
    color: #DC2626;
    font-size: 12px;
    font-weight: 900;
    line-height: 1;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 0;
  }

  /* Cup Tower Stack Container */
  .cup-stack-area {
    flex: 1;
    width: 100%;
    display: flex;
    flex-direction: column-reverse;
    align-items: center;
    justify-content: flex-start;
    padding: 6px 0 2px;
    position: relative;
    min-height: 0;
    overflow: hidden;
  }

  .pedestal-base {
    width: 86%;
    height: 10px;
    background: linear-gradient(90deg, #64748B, #94A3B8, #64748B);
    border: 2px solid #334155;
    border-radius: 6px;
    flex-shrink: 0;
  }

  .castle-cup {
    width: 68%;
    border: 2.5px solid;
    border-radius: 5px 5px 12px 12px;
    box-shadow: inset 0 -4px 0 rgba(0,0,0,0.18), 0 2px 4px rgba(0,0,0,0.2);
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 11px;
    font-weight: 900;
    color: #fff;
    position: relative;
    margin-top: -2px;
    flex-shrink: 0;
    animation: dropCup 0.28s cubic-bezier(0.34, 1.56, 0.64, 1);
  }

  @keyframes dropCup {
    0% { transform: translateY(-28px) scale(1.12); opacity: 0; }
    100% { transform: translateY(0) scale(1); opacity: 1; }
  }

  .leader-crown {
    position: absolute;
    bottom: 102%;
    font-size: 24px;
    line-height: 1;
    filter: drop-shadow(0 3px 4px rgba(0,0,0,0.3));
    animation: gentleBob 1.8s ease-in-out infinite;
    pointer-events: none;
  }

  .team-tap-btns {
    display: flex;
    gap: 4px;
    width: 100%;
    flex-shrink: 0;
  }

  .tap-cup-btn {
    flex: 1;
    border: 2.5px solid;
    border-radius: 11px;
    background: #FFFFFF;
    font-family: inherit;
    font-size: 11.5px;
    font-weight: 900;
    padding: 5px 2px;
    cursor: pointer;
    box-shadow: 0 3px 0 rgba(0,0,0,0.16);
    transition: transform 0.1s;
  }
  .tap-cup-btn:active {
    transform: translateY(2px);
    box-shadow: none;
  }

  /* Fireworks Canvas / Particles */
  .confetti-layer {
    position: absolute;
    inset: 0;
    pointer-events: none;
    z-index: 40;
    overflow: hidden;
  }

  .confetti-dot {
    position: absolute;
    width: 12px;
    height: 12px;
    border-radius: 50%;
    animation: burstOut 0.95s 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; }
  }

  /* Winner Modal */
  .modal-overlay {
    position: absolute;
    inset: 0;
    background: rgba(15, 23, 42, 0.72);
    backdrop-filter: blur(5px);
    z-index: 80;
    display: none;
    align-items: center;
    justify-content: center;
  }
  .modal-overlay.open {
    display: flex;
  }
  .winner-card {
    background: linear-gradient(180deg, #FFFBEB 0%, #FEF3C7 100%);
    border: 6px solid #F59E0B;
    border-radius: 30px;
    padding: 28px 38px;
    text-align: center;
    max-width: 520px;
    box-shadow: 0 25px 60px rgba(0, 0, 0, 0.5);
  }
</style>
</head>
<body>
<div class="viewport-shell">
  <div class="stage-1280" id="appStage">
    <div class="stage-bg-pattern"></div>

    <!-- 1. TOP BAR -->
    <header class="top-bar">
      <div class="brand-cluster">
        <div class="brand-logo" aria-hidden="true">👑</div>
        <div>
          <div class="brand-title">am, is, are</div>
          <div class="brand-sub">Verb to Be Kingdom &amp; Cup Castle Race</div>
        </div>
      </div>

      <nav class="mode-tabs" aria-label="Game Modes">
        <button class="mode-tab active" data-mode="bridge" type="button">
          <span>🧩</span> <span>1. Sentence Bridge</span>
        </button>
        <button class="mode-tab" data-mode="sort" type="button">
          <span>🧺</span> <span>2. Verb Basket Sort</span>
        </button>
        <button class="mode-tab" data-mode="flip" type="button">
          <span>🔄</span> <span>3. Question Flip</span>
        </button>
      </nav>

      <div class="top-right">
        <div class="star-pill" title="Total Correct Sentences">
          <span>⭐</span> <span id="starCount">0</span>
        </div>
        <button class="util-btn" id="repeatBtn" type="button" title="Repeat Question Audio">
          <span>🔊</span> Repeat
        </button>
        <button class="util-btn" id="resetBtn" type="button" title="Reset Scores">
          <span>🔄</span> Reset
        </button>
        <button class="util-btn" id="soundBtn" type="button" title="Toggle Sound">
          <span id="soundIcon">🔊</span> Sound
        </button>
        <button class="util-btn" id="fullBtn" type="button" title="Toggle Fullscreen">
          <span>⛶</span> Full
        </button>
      </div>
    </header>

    <!-- 2. INTERACTIVE PRONOUN GRAMMAR RULE HELPER & VOICE BAR -->
    <section class="grammar-bar" aria-label="Verb to Be Grammar Rules">
      <div class="rule-pills">
        <button class="rule-pill am" id="ruleBtnAm" type="button" title="Tap to hear the AM rule!">
          <span class="rp-verb">am</span>
          <span class="rp-subj">👑 Only with <b>I</b><small>"I am..." (Me!)</small></span>
        </button>
        <button class="rule-pill is" id="ruleBtnIs" type="button" title="Tap to hear the IS rule!">
          <span class="rp-verb">is</span>
          <span class="rp-subj">🌟 <b>1</b> Person or Thing<small>He / She / It / Singular</small></span>
        </button>
        <button class="rule-pill are" id="ruleBtnAre" type="button" title="Tap to hear the ARE rule!">
          <span class="rp-verb">are</span>
          <span class="rp-subj">🎉 <b>2+</b> Plural or You<small>We / They / You / Plural</small></span>
        </button>
      </div>

      <div class="voice-actions">
        <button class="act-btn" id="hearClueBtn" type="button">
          <span>🇺🇸</span> <span>Hear Clue</span>
        </button>
        <button class="act-btn uk" id="hearSentenceBtn" type="button">
          <span>🇬🇧</span> <span>Hear Sentence</span>
        </button>
        <button class="act-btn next" id="nextQuestionBtn" type="button">
          <span>➡️</span> <span>Next Card</span>
        </button>
      </div>
    </section>

    <!-- 3. SPLIT STAGE: LEFT GRAMMAR ARENA + RIGHT CUP CASTLE RACE -->
    <main class="main-split">
      <section class="grammar-stage" id="grammarStage" aria-label="Interactive Grammar Stage"></section>

      <aside class="race-stage" aria-label="3-Team Cup Castle Race">
        <div class="rs-head">
          <div class="rs-title">
            <span>🏰</span> <span>Cup Castle Race</span>
          </div>
          <button class="act-btn" id="whoWinsBtn" type="button" style="padding:3px 10px;font-size:12px">
            🏆 Who's Winning?
          </button>
        </div>

        <div class="towers-grid" id="towersGrid"></div>

        <div style="font-size:11.5px;font-weight:800;color:#1E3A8A;text-align:center;background:rgba(255,255,255,0.9);padding:5px;border-radius:11px;border:2px solid #93C5FD;z-index:3" id="raceStatusTxt">
          ✨ Answering stacks a cup on the active team's castle! Tap any team to switch turns!
        </div>
      </aside>
    </main>

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

    <!-- WINNER MODAL -->
    <div class="modal-overlay" id="winnerModal" role="dialog" aria-modal="true">
      <div class="winner-card">
        <div style="font-size:72px;line-height:1;margin-bottom:8px">👑</div>
        <h2 style="font-size:34px;font-weight:900;color:#92400E;margin:0 0 6px">Cup Castle Champions!</h2>
        <div style="font-size:25px;font-weight:900;color:#1E3A8A;margin:0 0 18px" id="winnerText">Team Ruby Wins!</div>
        <div style="display:flex;justify-content:center;gap:12px">
          <button class="act-btn" id="closeWinnerBtn" type="button">Keep Playing</button>
          <button class="act-btn uk" id="newRaceBtn" type="button">🔄 New Castle Race</button>
        </div>
      </div>
    </div>

  </div>
</div>

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

  /* ============ 1. PROPORTIONAL 1280x720 SCALE 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. ALL 24 SENTENCES & GRAMMAR METADATA ============ */
  var QUESTIONS = [
    // Community Town
    { id: "01", world: "community", worldName: "🚓 Community Town", emoji: "🚓", subjEmoji: "👮", subject: "The police officer", category: "Singular • Just 1 Person (He/She)", before: "The police officer 👮", after: "helping.", answer: "is", flipSubj: "the police officer 👮", flipAfter: "helping?", shortAns: "Yes, he is!" },
    { id: "02", world: "community", worldName: "🚓 Community Town", emoji: "📚", subjEmoji: "🧑‍🤝‍🧑", subject: "We", category: "Plural • 2+ People (Us Together)", before: "We 🧑‍🤝‍🧑", after: "at the library.", answer: "are", flipSubj: "we 🧑‍🤝‍🧑", flipAfter: "at the library?", shortAns: "Yes, we are!" },
    { id: "03", world: "community", worldName: "🚓 Community Town", emoji: "🛒", subjEmoji: "🙋", subject: "I", category: "First Person • Talking About Myself!", before: "I 🙋", after: "at the supermarket.", answer: "am", flipSubj: "I 🙋", flipAfter: "at the supermarket?", shortAns: "Yes, I am!" },
    { id: "04", world: "community", worldName: "🚓 Community Town", emoji: "🥖", subjEmoji: "🥐", subject: "The bakery", category: "Singular • Just 1 Place (It)", before: "The bakery 🥐", after: "smelling good.", answer: "is", flipSubj: "the bakery 🥐", flipAfter: "smelling good?", shortAns: "Yes, it is!" },
    { id: "05", world: "community", worldName: "🚓 Community Town", emoji: "🏞️", subjEmoji: "🌳", subject: "The park trees", category: "Plural • More Than 1 Tree (They)", before: "The park trees 🌳", after: "very tall.", answer: "are", flipSubj: "the park trees 🌳", flipAfter: "very tall?", shortAns: "Yes, they are!" },

    // Sunny Farm
    { id: "06", world: "farm", worldName: "🚜 Sunny Farm", emoji: "🚜", subjEmoji: "🐴", subject: "The horses", category: "Plural • More Than 1 Horse (They)", before: "The horses 🐴", after: "running fast.", answer: "are", flipSubj: "the horses 🐴", flipAfter: "running fast?", shortAns: "Yes, they are!" },
    { id: "07", world: "farm", worldName: "🚜 Sunny Farm", emoji: "🌱", subjEmoji: "🎃", subject: "The pumpkin", category: "Singular • Just 1 Pumpkin (It)", before: "The pumpkin 🎃", after: "growing in the soil.", answer: "is", flipSubj: "the pumpkin 🎃", flipAfter: "growing in the soil?", shortAns: "Yes, it is!" },
    { id: "08", world: "farm", worldName: "🚜 Sunny Farm", emoji: "🌻", subjEmoji: "🧑‍🌾", subject: "I", category: "First Person • Talking About Myself!", before: "I 🧑‍🌾", after: "planting seeds.", answer: "am", flipSubj: "I 🧑‍🌾", flipAfter: "planting seeds?", shortAns: "Yes, I am!" },
    { id: "09", world: "farm", worldName: "🚜 Sunny Farm", emoji: "🐑", subjEmoji: "🐑", subject: "The sheep", category: "Singular • 1 Fluffy Sheep (It)", before: "The sheep 🐑", after: "fluffy.", answer: "is", flipSubj: "the sheep 🐑", flipAfter: "fluffy?", shortAns: "Yes, it is!" },

    // Cozy Closet
    { id: "10", world: "clothing", worldName: "👕 Cozy Closet", emoji: "🧥", subjEmoji: "🥾", subject: "My boots", category: "Plural • 2 Muddy Boots (They)", before: "My boots 🥾", after: "muddy.", answer: "are", flipSubj: "my boots 🥾", flipAfter: "muddy?", shortAns: "Yes, they are!" },
    { id: "11", world: "clothing", worldName: "👕 Cozy Closet", emoji: "👗", subjEmoji: "👒", subject: "Her hat", category: "Singular • Just 1 Hat (It)", before: "Her hat 👒", after: "yellow.", answer: "is", flipSubj: "her hat 👒", flipAfter: "yellow?", shortAns: "Yes, it is!" },
    { id: "12", world: "clothing", worldName: "👕 Cozy Closet", emoji: "👕", subjEmoji: "👕", subject: "I", category: "First Person • Talking About Myself!", before: "I 👕", after: "wearing a blue shirt.", answer: "am", flipSubj: "I 👕", flipAfter: "wearing a blue shirt?", shortAns: "Yes, I am!" },
    { id: "13", world: "clothing", worldName: "👕 Cozy Closet", emoji: "🧦", subjEmoji: "🧦", subject: "These socks", category: "Plural • 2 Warm Socks (They)", before: "These socks 🧦", after: "warm.", answer: "are", flipSubj: "these socks 🧦", flipAfter: "warm?", shortAns: "Yes, they are!" },
    { id: "14", world: "clothing", worldName: "👕 Cozy Closet", emoji: "🧣", subjEmoji: "🧥", subject: "The jacket", category: "Singular • Just 1 Jacket (It)", before: "The jacket 🧥", after: "big.", answer: "is", flipSubj: "the jacket 🧥", flipAfter: "big?", shortAns: "Yes, it is!" },

    // Yummy Kitchen & Friends
    { id: "15", world: "food", worldName: "🍓 Food & Friends", emoji: "🥩", subjEmoji: "🐟", subject: "Fish", category: "Singular Food Group (It)", before: "Fish 🐟", after: "healthy protein.", answer: "is", flipSubj: "fish 🐟", flipAfter: "healthy protein?", shortAns: "Yes, it is!" },
    { id: "16", world: "food", worldName: "🍓 Food & Friends", emoji: "🥪", subjEmoji: "🍞", subject: "I", category: "First Person • Talking About Myself!", before: "I 🍞", after: "eating whole grain bread.", answer: "am", flipSubj: "I 🍞", flipAfter: "eating whole grain bread?", shortAns: "Yes, I am!" },
    { id: "17", world: "food", worldName: "🍓 Food & Friends", emoji: "🍎", subjEmoji: "🍓", subject: "The strawberries", category: "Plural • Lots of Berries (They)", before: "The strawberries 🍓", after: "sweet fruits.", answer: "are", flipSubj: "the strawberries 🍓", flipAfter: "sweet fruits?", shortAns: "Yes, they are!" },
    { id: "18", world: "food", worldName: "🍓 Food & Friends", emoji: "🧀", subjEmoji: "🥛", subject: "Milk", category: "Singular Drink (It)", before: "Milk 🥛", after: "good for my bones.", answer: "is", flipSubj: "milk 🥛", flipAfter: "good for my bones?", shortAns: "Yes, it is!" },
    { id: "19", world: "food", worldName: "🍓 Food & Friends", emoji: "🧥", subjEmoji: "👧", subject: "She", category: "Singular • Just 1 Girl (She)", before: "She 👧", after: "wearing a pink raincoat.", answer: "is", flipSubj: "she 👧", flipAfter: "wearing a pink raincoat?", shortAns: "Yes, she is!" },
    { id: "20", world: "food", worldName: "🍓 Food & Friends", emoji: "🐑", subjEmoji: "🐑", subject: "Three little lambs", category: "Plural • 3 Little Lambs (2+)", before: "There", after: "three little lambs 🐑🐑🐑.", answer: "are", flipSubj: "there", flipAfter: "three little lambs 🐑🐑🐑?", shortAns: "Yes, there are!" },
    { id: "21", world: "food", worldName: "🍓 Food & Friends", emoji: "🥕", subjEmoji: "🙋", subject: "I", category: "First Person • Talking About Myself!", before: "I 🙋", after: "picking carrots for my rabbit 🐇.", answer: "am", flipSubj: "I 🙋", flipAfter: "picking carrots for my rabbit 🐇?", shortAns: "Yes, I am!" },
    { id: "22", world: "food", worldName: "🍓 Food & Friends", emoji: "🥚", subjEmoji: "🐊", subject: "A baby crocodile", category: "Singular • 1 Baby Crocodile (It)", before: "There", after: "a baby crocodile 🐊 inside the egg.", answer: "is", flipSubj: "there", flipAfter: "a baby crocodile 🐊 inside the egg?", shortAns: "Yes, there is!" },
    { id: "23", world: "food", worldName: "🍓 Food & Friends", emoji: "🏥", subjEmoji: "👩", subject: "My mother", category: "Singular • 1 Person (She)", before: "My mother 👩", after: "going to the hospital to see a doctor.", answer: "is", flipSubj: "my mother 👩", flipAfter: "going to the hospital?", shortAns: "Yes, she is!" },
    { id: "24", world: "food", worldName: "🍓 Food & Friends", emoji: "📚", subjEmoji: "🧑‍🤝‍🧑", subject: "They", category: "Plural • More Than 1 Person (They)", before: "They 🧑‍🤝‍🧑", after: "at the library to read books 📚.", answer: "are", flipSubj: "they 🧑‍🤝‍🧑", flipAfter: "at the library to read books 📚?", shortAns: "Yes, they are!" }
  ];

  var TEAMS = [
    { name: "Team Ruby",     icon: "🔴", c: "#EF4444", bg: "linear-gradient(180deg, #F87171, #DC2626)", bd: "#991B1B" },
    { name: "Team Sapphire", icon: "🔵", c: "#2563EB", bg: "linear-gradient(180deg, #60A5FA, #2563EB)", bd: "#1E3A8A" },
    { name: "Team Emerald",  icon: "🟢", c: "#10B981", bg: "linear-gradient(180deg, #34D399, #059669)", bd: "#064E3B" }
  ];

  /* ============ 3. STATE ============ */
  var state = {
    mode: "bridge",           // "bridge" | "sort" | "flip"
    worldFilter: "all",       // "all" | "community" | "farm" | "clothing" | "food"
    qIdx: 0,
    solved: false,
    wrongGuesses: [],
    streak: 0,
    stars: 0,
    activeTeam: 0,
    teamScores: [0, 0, 0],
    soundOn: true,
    audioCtx: null,
    decoded: {},
    activeSources: [],
    activeHtmlAudio: null
  };

  function getFilteredList() {
    if (state.worldFilter === "all") return QUESTIONS;
    return QUESTIONS.filter(function(item) { return item.world === state.worldFilter; });
  }

  function currentItem() {
    var list = getFilteredList();
    if (!list.length) return QUESTIONS[0];
    var idx = ((state.qIdx % list.length) + list.length) % list.length;
    return list[idx];
  }

  /* ============ 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.32, t + (dur || 0.15));
      g.gain.setValueAtTime(0.0001, t);
      g.gain.exponentialRampToValueAtTime(0.09, t + 0.015);
      g.gain.exponentialRampToValueAtTime(0.0001, t + (dur || 0.15));
      o.connect(g);
      g.connect(ctx.destination);
      o.start(t);
      o.stop(t + (dur || 0.15) + 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.AM_IS_ARE_AUDIO && window.AM_IS_ARE_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.AM_IS_ARE_AUDIO && window.AM_IS_ARE_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.05;
        });
        return;
      } catch (e) {}
    }

    for (var i = 0; i < valid.length; i++) {
      var item = window.AM_IS_ARE_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]);
  }

  /* ============ 5. CONFETTI / FIREWORKS BURST ============ */
  function triggerFireworks() {
    var layer = document.getElementById("confettiLayer");
    if (!layer) return;
    var colors = ["#F59E0B", "#EC4899", "#3B82F6", "#10B981", "#A855F7", "#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() * 220;
      dot.style.left = "42%";
      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);
      }, 1000, dot);
    }
  }

  /* ============ 6. RENDER GRAMMAR RULE PILLS HIGHLIGHT ============ */
  function updateRuleHighlight() {
    var item = currentItem();
    ["am", "is", "are"].forEach(function(v) {
      var btn = document.getElementById("ruleBtn" + v.charAt(0).toUpperCase() + v.slice(1));
      if (!btn) return;
      if (state.solved && item.answer === v) {
        btn.classList.add("highlight");
      } else {
        btn.classList.remove("highlight");
      }
    });
  }

  /* ============ 7. HANDLE OPTION PICK across all 3 modes ============ */
  function handlePickVerb(selected) {
    if (state.solved) return;
    var item = currentItem();
    if (selected === item.answer) {
      state.solved = true;
      state.stars += 1;
      state.streak += 1;
      document.getElementById("starCount").textContent = String(state.stars);

      // Award a Cup to the active team and advance active team turn!
      var rewardedTeam = state.activeTeam;
      state.teamScores[rewardedTeam] += 1;
      state.activeTeam = (state.activeTeam + 1) % 3;

      playTone(587.33, 0.14, "sine");
      setTimeout(function() { playTone(880, 0.18, "triangle"); }, 90);
      triggerFireworks();

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

      // Speak the completed or flipped sentence with British Male Ryan!
      if (state.mode === "flip") {
        playClip("flip_" + item.id);
      } else {
        playClip("ans_" + item.id);
      }

      renderAll();
    } else {
      if (state.wrongGuesses.indexOf(selected) === -1) {
        state.wrongGuesses.push(selected);
      }
      state.streak = 0;
      playTone(210, 0.22, "sawtooth");
      playClip("try_again");
      renderStage();
    }
  }

  function nextQuestion(speakClueAfter) {
    var list = getFilteredList();
    state.qIdx = (state.qIdx + 1) % list.length;
    state.solved = false;
    state.wrongGuesses = [];
    renderAll();
    if (speakClueAfter) {
      speakCurrentPrompt();
    }
  }

  function speakCurrentPrompt() {
    var item = currentItem();
    if (state.mode === "sort") {
      playClip("sort_" + item.id);
    } else if (state.mode === "flip" && state.solved) {
      playClip("flip_" + item.id);
    } else if (state.solved) {
      playClip("ans_" + item.id);
    } else {
      playClip("q_" + item.id);
    }
  }

  /* ============ 8. RENDER LEFT GRAMMAR STAGE ============ */
  function renderThemeHeader(list) {
    var idx = ((state.qIdx % list.length) + list.length) % list.length;
    var worlds = [
      { id: "all", lbl: "🌎 All 24 Cards" },
      { id: "community", lbl: "🚓 Community" },
      { id: "farm", lbl: "🚜 Sunny Farm" },
      { id: "clothing", lbl: "👕 Cozy Closet" },
      { id: "food", lbl: "🍓 Food & Friends" }
    ];
    var chipsHtml = worlds.map(function(w) {
      var active = state.worldFilter === w.id ? " active" : "";
      return '<button type="button" class="theme-chip' + active + '" data-world="' + w.id + '">' + w.lbl + '</button>';
    }).join("");

    return (
      '<div class="theme-row">' +
        '<div class="theme-chips">' + chipsHtml + '</div>' +
        '<div class="q-counter-badge">Card ' + (idx + 1) + ' of ' + list.length + ' • Streak 🔥 ' + state.streak + '</div>' +
      '</div>'
    );
  }

  function renderBridgeMode(stage, item, list) {
    var slotClass = "verb-slot" + (state.solved ? (" filled " + item.answer) : "");
    var slotText = state.solved ? item.answer : "___";

    var options = [
      { v: "am", hint: "👑 Only with I" },
      { v: "is", hint: "🌟 1 Person / Thing" },
      { v: "are", hint: "🎉 2+ Plural / You" }
    ];

    var btnsHtml = options.map(function(o) {
      var isWrong = state.wrongGuesses.indexOf(o.v) !== -1;
      var isCorrect = state.solved && item.answer === o.v;
      var cls = "verb-choice-btn " + o.v + (isWrong ? " wrong" : "") + (isCorrect ? " correct" : "");
      var dis = (isWrong || state.solved) ? " disabled" : "";
      return (
        '<button type="button" class="' + cls + '" data-pick="' + o.v + '"' + dis + '>' +
          '<span class="vc-word">' + o.v + '</span>' +
          '<span class="vc-hint">' + o.hint + '</span>' +
        '</button>'
      );
    }).join("");

    stage.innerHTML =
      renderThemeHeader(list) +
      '<div class="scene-card">' +
        '<div class="detective-row">' +
          '<span class="subj-badge ' + item.answer + '">🔎 Subject Detective: <b>"' + item.subject + '"</b> &bull; ' + item.category + '</span>' +
        '</div>' +
        '<div class="scene-illustration">' +
          '<div class="big-emoji-bubble">' + item.subjEmoji + '</div>' +
          '<div style="font-size:32px;font-weight:900;color:#94A3B8">+</div>' +
          '<div class="big-emoji-bubble" style="border-color:#F59E0B">' + item.emoji + '</div>' +
        '</div>' +
        '<div class="sentence-bridge-box">' +
          '<span class="subj-highlight">' + item.before + '</span>' +
          '<span class="' + slotClass + '">' + slotText + '</span>' +
          '<span>' + item.after + '</span>' +
        '</div>' +
      '</div>' +
      '<div class="choices-row">' + btnsHtml + '</div>';
  }

  function renderSortMode(stage, item, list) {
    var baskets = [
      { v: "am", icon: "👑", title: "AM Basket", sub: "Only for I (Myself!)" },
      { v: "is", icon: "🌟", title: "IS Basket", sub: "He / She / It (Just 1)" },
      { v: "are", icon: "🎉", title: "ARE Basket", sub: "We / They / You (2+)" }
    ];

    var basketsHtml = baskets.map(function(b) {
      var isWrong = state.wrongGuesses.indexOf(b.v) !== -1;
      var isCorrect = state.solved && item.answer === b.v;
      var extraStyle = isWrong ? "opacity:0.4;transform:scale(0.95);" : (isCorrect ? "border-color:#16A34A;background:linear-gradient(180deg,#86EFAC,#22C55E);color:#fff;" : "");
      var dis = (isWrong || state.solved) ? " disabled" : "";
      return (
        '<button type="button" class="basket-btn ' + b.v + '" style="' + extraStyle + '" data-pick="' + b.v + '"' + dis + '>' +
          '<span class="basket-icon">' + b.icon + ' 🧺</span>' +
          '<span class="basket-title">' + b.title + '</span>' +
          '<span class="basket-sub">' + b.sub + '</span>' +
        '</button>'
      );
    }).join("");

    var feedbackHtml = state.solved
      ? '<div style="font-size:22px;font-weight:900;color:#047857;background:#D1FAE5;border:3px solid #34D399;padding:8px 20px;border-radius:16px">✅ ' + item.before + ' <u style="color:#1E40AF">' + item.answer.toUpperCase() + '</u> ' + item.after + '</div>'
      : '<div style="font-size:16px;font-weight:800;color:#475569">👇 Tap the matching Verb Basket below to toss this subject bubble in!</div>';

    stage.innerHTML =
      renderThemeHeader(list) +
      '<div class="sort-stage-wrap">' +
        '<div class="mystery-bubble-card">' +
          '<div style="font-size:13.5px;font-weight:900;color:#2563EB;text-transform:uppercase;letter-spacing:0.6px">🫧 Mystery Subject Bubble</div>' +
          '<div class="subject-float-pill">' +
            '<span class="sf-emoji">' + item.subjEmoji + '</span>' +
            '<span class="sf-text">' + item.subject + '</span>' +
          '</div>' +
          '<div class="subj-badge ' + item.answer + '">' + item.category + '</div>' +
          feedbackHtml +
        '</div>' +
        '<div class="baskets-row">' + basketsHtml + '</div>' +
      '</div>';
  }

  function renderFlipMode(stage, item, list) {
    var capAnswer = item.answer.charAt(0).toUpperCase() + item.answer.slice(1);
    var slotClass = "verb-slot" + (state.solved ? (" filled " + item.answer) : "");
    var slotText = state.solved ? capAnswer : "___";

    var options = [
      { v: "am", word: "Am", hint: "👑 Am I...?" },
      { v: "is", word: "Is", hint: "🌟 Is he / she / it...?" },
      { v: "are", word: "Are", hint: "🎉 Are we / they / you...?" }
    ];

    var btnsHtml = options.map(function(o) {
      var isWrong = state.wrongGuesses.indexOf(o.v) !== -1;
      var isCorrect = state.solved && item.answer === o.v;
      var cls = "verb-choice-btn " + o.v + (isWrong ? " wrong" : "") + (isCorrect ? " correct" : "");
      var dis = (isWrong || state.solved) ? " disabled" : "";
      return (
        '<button type="button" class="' + cls + '" data-pick="' + o.v + '"' + dis + '>' +
          '<span class="vc-word">' + o.word + '...?</span>' +
          '<span class="vc-hint">' + o.hint + '</span>' +
        '</button>'
      );
    }).join("");

    stage.innerHTML =
      renderThemeHeader(list) +
      '<div class="scene-card">' +
        '<div style="font-size:14px;font-weight:900;color:#6D28D9;background:#F3E8FF;border:2px solid #C084FC;padding:5px 14px;border-radius:999px">' +
          '🔄 Statement: <b>"' + item.before + ' <span style="color:#2563EB;text-decoration:underline">' + item.answer + '</span> ' + item.after + '"</b> &rarr; Flip the verb to the front!' +
        '</div>' +
        '<div class="sentence-bridge-box">' +
          '<span class="' + slotClass + '">' + slotText + '</span>' +
          '<span class="subj-highlight">' + item.flipSubj + '</span>' +
          '<span>' + item.flipAfter + '</span>' +
        '</div>' +
        (state.solved
          ? '<div style="font-size:22px;font-weight:900;color:#065F46;background:#D1FAE5;border:3px solid #34D399;padding:6px 18px;border-radius:14px">🇬🇧 Short Answer: <b>"' + item.shortAns + '"</b> ' + item.emoji + '</div>'
          : '<div style="font-size:14.5px;font-weight:800;color:#475569">💡 Which verb jumps to the very beginning to turn this into a question?</div>'
        ) +
      '</div>' +
      '<div class="choices-row">' + btnsHtml + '</div>';
  }

  function renderStage() {
    var stage = document.getElementById("grammarStage");
    if (!stage) return;
    var list = getFilteredList();
    var item = currentItem();

    if (state.mode === "sort") {
      renderSortMode(stage, item, list);
    } else if (state.mode === "flip") {
      renderFlipMode(stage, item, list);
    } else {
      renderBridgeMode(stage, item, list);
    }

    // Bind world filter chips
    stage.querySelectorAll("[data-world]").forEach(function(btn) {
      btn.addEventListener("click", function() {
        state.worldFilter = btn.getAttribute("data-world");
        state.qIdx = 0;
        state.solved = false;
        state.wrongGuesses = [];
        playTone(520, 0.09, "sine");
        renderAll();
      });
    });

    // Bind verb pick buttons
    stage.querySelectorAll("[data-pick]").forEach(function(btn) {
      btn.addEventListener("click", function() {
        handlePickVerb(btn.getAttribute("data-pick"));
      });
    });

    updateRuleHighlight();
  }

  /* ============ 9. RENDER RIGHT 3-TEAM CUP CASTLE RACE ============ */
  function renderRaceTowers() {
    var grid = document.getElementById("towersGrid");
    if (!grid) return;

    var maxScore = Math.max.apply(null, state.teamScores);
    var scaleMax = Math.max(8, maxScore + 1);

    grid.innerHTML = TEAMS.map(function(t, idx) {
      var score = state.teamScores[idx];
      var isLeader = maxScore > 0 && score === maxScore;
      var isTurn = state.activeTeam === idx;

      var cupsHtml = "";
      for (var c = 0; c < score; c++) {
        var isTop = c === score - 1;
        var hPct = Math.max(16, Math.floor(270 / scaleMax));
        cupsHtml +=
          '<div class="castle-cup" style="height:' + hPct + 'px;background:' + t.bg + ';border-color:' + t.bd + ';z-index:' + (c + 2) + '">' +
            (scaleMax <= 18 ? (c + 1) : "") +
            (isTop && isLeader ? '<span class="leader-crown">👑</span>' : "") +
          '</div>';
      }

      return (
        '<div class="team-column' + (isTurn ? ' active-turn' : '') + '" data-team-col="' + idx + '">' +
          '<div class="team-top-pill" style="border-color:' + t.c + '">' +
            '<div class="tt-name" style="color:' + t.c + '">' + t.icon + ' ' + t.name + '</div>' +
            '<div class="tt-score-row">' +
              '<span>🥤 ' + score + '</span>' +
              '<button type="button" class="minus-cup-btn" data-minus="' + idx + '" title="Remove 1 cup">-</button>' +
            '</div>' +
          '</div>' +
          '<div class="cup-stack-area">' +
            '<div class="pedestal-base"></div>' +
            cupsHtml +
          '</div>' +
          '<div class="team-tap-btns">' +
            '<button type="button" class="tap-cup-btn" style="border-color:' + t.c + ';color:' + t.c + '" data-add="' + idx + '">✋ +1 Cup</button>' +
          '</div>' +
        '</div>'
      );
    }).join("");

    // Bind column click to switch active team
    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);
        playTone(440, 0.08, "sine");
        renderRaceTowers();
      });
    });

    // Bind +1 cup button
    grid.querySelectorAll("[data-add]").forEach(function(btn) {
      btn.addEventListener("click", function(e) {
        e.stopPropagation();
        var ti = parseInt(btn.getAttribute("data-add"), 10);
        state.teamScores[ti] += 1;
        playTone(620, 0.12, "triangle");
        playClip("praise_" + (ti + 1));
        renderRaceTowers();
      });
    });

    // Bind -1 cup button
    grid.querySelectorAll("[data-minus]").forEach(function(btn) {
      btn.addEventListener("click", function(e) {
        e.stopPropagation();
        var ti = parseInt(btn.getAttribute("data-minus"), 10);
        state.teamScores[ti] = Math.max(0, state.teamScores[ti] - 1);
        playTone(280, 0.09, "sine");
        renderRaceTowers();
      });
    });
  }

  function renderAll() {
    renderStage();
    renderRaceTowers();
  }

  /* ============ 10. WIRE GLOBAL CONTROLS ============ */
  document.querySelectorAll(".mode-tab").forEach(function(tab) {
    tab.addEventListener("click", function() {
      var m = tab.getAttribute("data-mode");
      state.mode = m;
      state.solved = false;
      state.wrongGuesses = [];
      document.querySelectorAll(".mode-tab").forEach(function(t) {
        t.classList.toggle("active", t === tab);
      });
      if (m === "bridge") playClip("mode_bridge");
      else if (m === "sort") playClip("mode_sort");
      else if (m === "flip") playClip("mode_flip");
      renderAll();
    });
  });

  document.getElementById("ruleBtnAm").addEventListener("click", function() {
    playClip("rule_am");
  });
  document.getElementById("ruleBtnIs").addEventListener("click", function() {
    playClip("rule_is");
  });
  document.getElementById("ruleBtnAre").addEventListener("click", function() {
    playClip("rule_are");
  });

  document.getElementById("hearClueBtn").addEventListener("click", function() {
    var item = currentItem();
    playClip(state.mode === "sort" ? ("sort_" + item.id) : ("q_" + item.id));
  });

  document.getElementById("hearSentenceBtn").addEventListener("click", function() {
    var item = currentItem();
    playClip(state.mode === "flip" ? ("flip_" + item.id) : ("ans_" + item.id));
  });

  document.getElementById("nextQuestionBtn").addEventListener("click", function() {
    nextQuestion(true);
  });

  document.getElementById("repeatBtn").addEventListener("click", function() {
    speakCurrentPrompt();
  });

  document.getElementById("resetBtn").addEventListener("click", function() {
    state.teamScores = [0, 0, 0];
    state.stars = 0;
    state.streak = 0;
    state.solved = false;
    state.wrongGuesses = [];
    document.getElementById("starCount").textContent = "0";
    playTone(360, 0.12, "sine");
    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 cups first to crown a champion!"
      : names + " (" + max + " Cups!)";
    document.getElementById("winnerModal").classList.add("open");
    triggerFireworks();
    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");
    renderRaceTowers();
  });

  // Initial render
  renderAll();
})();
</script>
</body>
</html>