23656 lines
1.3 MiB
23656 lines
1.3 MiB
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>Soroban Flashcards</title>
|
||
<style>
|
||
/* CSS @property declarations for smooth gradient transitions */
|
||
@property --sky-gradient {
|
||
syntax: '<image>';
|
||
inherits: false;
|
||
initial-value: linear-gradient(135deg, #ffb347 0%, #ffcc5c 50%, #87ceeb 100%);
|
||
}
|
||
|
||
/* Time-of-day gradient definitions */
|
||
:root {
|
||
--dawn-gradient: linear-gradient(135deg, #ffb347 0%, #ffcc5c 30%, #87ceeb 70%, #98d8e8 100%);
|
||
--morning-gradient: linear-gradient(135deg, #87ceeb 0%, #98d8e8 30%, #b6e2ff 70%, #cce7ff 100%);
|
||
--midday-gradient: linear-gradient(135deg, #87ceeb 0%, #a8d8ea 30%, #c7e2f7 70%, #e3f2fd 100%);
|
||
--afternoon-gradient: linear-gradient(135deg, #ffecd2 0%, #fcb69f 30%, #ff8a65 70%, #ff7043 100%);
|
||
--dusk-gradient: linear-gradient(135deg, #ff8a65 0%, #ff7043 30%, #8e44ad 70%, #5b2c87 100%);
|
||
--night-gradient: linear-gradient(135deg, #2c3e50 0%, #34495e 30%, #1a252f 70%, #0f1419 100%);
|
||
}
|
||
|
||
body {
|
||
font-family: DejaVu Sans, sans-serif;
|
||
margin: 0;
|
||
padding: 0;
|
||
background: #f8fafc;
|
||
line-height: 1.6;
|
||
color: #2d3748;
|
||
}
|
||
|
||
.container {
|
||
max-width: 1400px;
|
||
margin: 0 auto;
|
||
padding: 0;
|
||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||
min-height: 100vh;
|
||
}
|
||
|
||
/* Beautiful Header */
|
||
.main-header {
|
||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||
color: white;
|
||
text-align: center;
|
||
padding: 60px 40px 40px;
|
||
position: relative;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.main-header::before {
|
||
content: '';
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
bottom: 0;
|
||
background: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><defs><pattern id="grain" width="100" height="100" patternUnits="userSpaceOnUse"><circle cx="25" cy="25" r="1" fill="white" opacity="0.1"/><circle cx="75" cy="75" r="1" fill="white" opacity="0.1"/><circle cx="50" cy="10" r="0.5" fill="white" opacity="0.15"/></pattern></defs><rect width="100" height="100" fill="url(%23grain)"/></svg>');
|
||
pointer-events: none;
|
||
}
|
||
|
||
.main-header h1 {
|
||
font-size: 3.5rem;
|
||
font-weight: 700;
|
||
margin: 0 0 15px 0;
|
||
text-shadow: 0 4px 20px rgba(0,0,0,0.3);
|
||
letter-spacing: -0.02em;
|
||
position: relative;
|
||
z-index: 1;
|
||
}
|
||
|
||
.main-header p {
|
||
font-size: 1.3rem;
|
||
margin: 0;
|
||
opacity: 0.95;
|
||
font-weight: 300;
|
||
max-width: 600px;
|
||
margin: 0 auto;
|
||
line-height: 1.6;
|
||
position: relative;
|
||
z-index: 1;
|
||
}
|
||
|
||
/* Modern Navigation */
|
||
.page-nav {
|
||
position: sticky;
|
||
top: 0;
|
||
background: rgba(255, 255, 255, 0.95);
|
||
backdrop-filter: blur(10px);
|
||
border-bottom: 1px solid rgba(102, 126, 234, 0.2);
|
||
z-index: 1000;
|
||
padding: 20px 40px;
|
||
box-shadow: 0 8px 32px rgba(0,0,0,0.1);
|
||
}
|
||
|
||
.nav-buttons {
|
||
display: flex;
|
||
gap: 8px;
|
||
justify-content: center;
|
||
max-width: 800px;
|
||
margin: 0 auto;
|
||
flex-wrap: wrap;
|
||
}
|
||
|
||
.nav-btn {
|
||
padding: 12px 24px;
|
||
border: none;
|
||
background: rgba(102, 126, 234, 0.1);
|
||
color: #4a5568;
|
||
border-radius: 50px;
|
||
cursor: pointer;
|
||
font-size: 15px;
|
||
font-weight: 600;
|
||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||
text-decoration: none;
|
||
position: relative;
|
||
overflow: hidden;
|
||
min-width: 140px;
|
||
text-align: center;
|
||
}
|
||
|
||
.nav-btn::before {
|
||
content: '';
|
||
position: absolute;
|
||
top: 0;
|
||
left: -100%;
|
||
width: 100%;
|
||
height: 100%;
|
||
background: linear-gradient(90deg, transparent, rgba(255,255,255,0.4), transparent);
|
||
transition: left 0.6s;
|
||
}
|
||
|
||
.nav-btn:hover {
|
||
background: rgba(102, 126, 234, 0.15);
|
||
transform: translateY(-2px);
|
||
box-shadow: 0 8px 25px rgba(102, 126, 234, 0.2);
|
||
}
|
||
|
||
.nav-btn:hover::before {
|
||
left: 100%;
|
||
}
|
||
|
||
.nav-btn.active {
|
||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||
color: white;
|
||
box-shadow: 0 8px 25px rgba(102, 126, 234, 0.4);
|
||
}
|
||
|
||
.nav-btn.active:hover {
|
||
transform: translateY(-2px);
|
||
box-shadow: 0 12px 35px rgba(102, 126, 234, 0.5);
|
||
}
|
||
|
||
/* Content Area */
|
||
.content-area {
|
||
background: white;
|
||
margin: 0;
|
||
padding: 0;
|
||
min-height: calc(100vh - 200px);
|
||
}
|
||
|
||
/* Section Styles */
|
||
.page-section {
|
||
display: none;
|
||
animation: fadeInUp 0.6s cubic-bezier(0.4, 0, 0.2, 1);
|
||
padding: 60px 40px 80px;
|
||
max-width: 1200px;
|
||
margin: 0 auto;
|
||
}
|
||
|
||
.page-section.active {
|
||
display: block;
|
||
}
|
||
|
||
@keyframes fadeInUp {
|
||
from {
|
||
opacity: 0;
|
||
transform: translateY(30px);
|
||
}
|
||
to {
|
||
opacity: 1;
|
||
transform: translateY(0);
|
||
}
|
||
}
|
||
|
||
.section-header {
|
||
text-align: center;
|
||
margin-bottom: 60px;
|
||
position: relative;
|
||
}
|
||
|
||
.section-title {
|
||
font-size: 2.8rem;
|
||
font-weight: 700;
|
||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||
-webkit-background-clip: text;
|
||
-webkit-text-fill-color: transparent;
|
||
background-clip: text;
|
||
margin: 0 0 15px 0;
|
||
letter-spacing: -0.02em;
|
||
}
|
||
|
||
.section-subtitle {
|
||
color: #718096;
|
||
font-size: 1.2rem;
|
||
margin: 0;
|
||
font-weight: 400;
|
||
max-width: 600px;
|
||
margin: 0 auto;
|
||
line-height: 1.6;
|
||
}
|
||
|
||
/* Welcome Section Styles */
|
||
.welcome-hero {
|
||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||
color: white;
|
||
margin: -60px -40px 0;
|
||
padding: 80px 40px 60px;
|
||
text-align: center;
|
||
position: relative;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.welcome-hero::before {
|
||
content: '';
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
bottom: 0;
|
||
background: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><circle cx="20" cy="20" r="2" fill="white" opacity="0.1"/><circle cx="80" cy="30" r="1.5" fill="white" opacity="0.1"/><circle cx="60" cy="70" r="1" fill="white" opacity="0.15"/><circle cx="30" cy="80" r="2.5" fill="white" opacity="0.08"/><circle cx="90" cy="80" r="1" fill="white" opacity="0.1"/></svg>');
|
||
animation: float 20s ease-in-out infinite;
|
||
}
|
||
|
||
@keyframes float {
|
||
0%, 100% { transform: translateY(0px); }
|
||
50% { transform: translateY(-10px); }
|
||
}
|
||
|
||
.welcome-content {
|
||
position: relative;
|
||
z-index: 1;
|
||
max-width: 1200px;
|
||
margin: 0 auto;
|
||
}
|
||
|
||
.welcome-title {
|
||
font-size: 3.5rem;
|
||
font-weight: 800;
|
||
margin-bottom: 20px;
|
||
text-shadow: 2px 2px 4px rgba(0,0,0,0.1);
|
||
}
|
||
|
||
.welcome-subtitle {
|
||
font-size: 1.4rem;
|
||
margin-bottom: 60px;
|
||
opacity: 0.95;
|
||
font-weight: 300;
|
||
line-height: 1.6;
|
||
}
|
||
|
||
.feature-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
|
||
gap: 30px;
|
||
margin: 60px 0;
|
||
padding: 0 20px;
|
||
}
|
||
|
||
.feature-card {
|
||
background: rgba(255, 255, 255, 0.95);
|
||
backdrop-filter: blur(10px);
|
||
border-radius: 20px;
|
||
padding: 40px 30px;
|
||
text-align: center;
|
||
color: #333;
|
||
box-shadow: 0 20px 40px rgba(0,0,0,0.1);
|
||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||
}
|
||
|
||
.feature-card:hover {
|
||
transform: translateY(-10px);
|
||
box-shadow: 0 30px 60px rgba(0,0,0,0.15);
|
||
}
|
||
|
||
.feature-icon {
|
||
font-size: 3rem;
|
||
margin-bottom: 20px;
|
||
display: block;
|
||
}
|
||
|
||
.feature-card h3 {
|
||
font-size: 1.5rem;
|
||
font-weight: 600;
|
||
margin-bottom: 15px;
|
||
color: #333;
|
||
}
|
||
|
||
.feature-card p {
|
||
color: #666;
|
||
line-height: 1.6;
|
||
margin-bottom: 25px;
|
||
}
|
||
|
||
.feature-btn {
|
||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||
color: white;
|
||
border: none;
|
||
padding: 12px 24px;
|
||
border-radius: 25px;
|
||
font-weight: 600;
|
||
cursor: pointer;
|
||
transition: all 0.3s ease;
|
||
font-size: 1rem;
|
||
}
|
||
|
||
.feature-btn:hover {
|
||
transform: translateY(-2px);
|
||
box-shadow: 0 8px 20px rgba(102, 126, 234, 0.3);
|
||
}
|
||
|
||
.quick-start {
|
||
background: rgba(255, 255, 255, 0.1);
|
||
border-radius: 20px;
|
||
padding: 50px 40px;
|
||
margin: 60px 20px 40px;
|
||
backdrop-filter: blur(10px);
|
||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||
}
|
||
|
||
.quick-start h2 {
|
||
font-size: 2.2rem;
|
||
font-weight: 700;
|
||
margin-bottom: 40px;
|
||
text-align: center;
|
||
}
|
||
|
||
.steps {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||
gap: 30px;
|
||
}
|
||
|
||
.step {
|
||
display: flex;
|
||
align-items: flex-start;
|
||
gap: 20px;
|
||
}
|
||
|
||
.step-number {
|
||
background: rgba(255, 255, 255, 0.2);
|
||
width: 50px;
|
||
height: 50px;
|
||
border-radius: 50%;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-weight: 700;
|
||
font-size: 1.3rem;
|
||
flex-shrink: 0;
|
||
border: 2px solid rgba(255, 255, 255, 0.3);
|
||
}
|
||
|
||
.step-content h4 {
|
||
font-size: 1.3rem;
|
||
font-weight: 600;
|
||
margin-bottom: 10px;
|
||
}
|
||
|
||
.step-content p {
|
||
opacity: 0.9;
|
||
line-height: 1.6;
|
||
}
|
||
|
||
.stats-preview {
|
||
background: rgba(255, 255, 255, 0.1);
|
||
border-radius: 20px;
|
||
padding: 40px;
|
||
margin: 40px 20px 0;
|
||
backdrop-filter: blur(10px);
|
||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||
text-align: center;
|
||
}
|
||
|
||
.stats-preview h3 {
|
||
font-size: 2rem;
|
||
font-weight: 700;
|
||
margin-bottom: 30px;
|
||
}
|
||
|
||
.stats-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||
gap: 30px;
|
||
}
|
||
|
||
.stat-item {
|
||
text-align: center;
|
||
}
|
||
|
||
.stat-number {
|
||
font-size: 3rem;
|
||
font-weight: 800;
|
||
margin-bottom: 10px;
|
||
background: rgba(255, 255, 255, 0.2);
|
||
border-radius: 15px;
|
||
padding: 20px;
|
||
display: inline-block;
|
||
min-width: 80px;
|
||
}
|
||
|
||
.stat-label {
|
||
font-size: 1.1rem;
|
||
opacity: 0.9;
|
||
font-weight: 500;
|
||
}
|
||
|
||
/* Responsive adjustments */
|
||
@media (max-width: 768px) {
|
||
.welcome-title {
|
||
font-size: 2.5rem;
|
||
}
|
||
|
||
.welcome-subtitle {
|
||
font-size: 1.2rem;
|
||
}
|
||
|
||
.feature-grid {
|
||
grid-template-columns: 1fr;
|
||
gap: 20px;
|
||
padding: 0 10px;
|
||
}
|
||
|
||
.quick-start {
|
||
margin: 40px 10px 20px;
|
||
padding: 30px 20px;
|
||
}
|
||
|
||
.steps {
|
||
grid-template-columns: 1fr;
|
||
gap: 20px;
|
||
}
|
||
|
||
.stats-grid {
|
||
grid-template-columns: repeat(2, 1fr);
|
||
gap: 20px;
|
||
}
|
||
}
|
||
|
||
.section-header::after {
|
||
content: '';
|
||
display: block;
|
||
width: 60px;
|
||
height: 4px;
|
||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||
margin: 30px auto 0;
|
||
border-radius: 2px;
|
||
}
|
||
|
||
.header {
|
||
text-align: center;
|
||
margin-bottom: 30px;
|
||
}
|
||
|
||
.header h1 {
|
||
color: #333;
|
||
font-size: 2.5em;
|
||
margin-bottom: 10px;
|
||
}
|
||
|
||
.header p {
|
||
color: #666;
|
||
font-size: 1.2em;
|
||
}
|
||
|
||
.cards-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
|
||
gap: 20px;
|
||
margin: 20px 0;
|
||
}
|
||
|
||
.flashcard {
|
||
background: white;
|
||
border-radius: 12px;
|
||
padding: 20px;
|
||
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
|
||
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
||
cursor: pointer;
|
||
position: relative;
|
||
min-height: 220px;
|
||
display: flex;
|
||
flex-direction: column;
|
||
justify-content: center;
|
||
align-items: center;
|
||
}
|
||
|
||
.flashcard:hover {
|
||
transform: translateY(-2px);
|
||
box-shadow: 0 8px 16px rgba(0,0,0,0.15);
|
||
}
|
||
|
||
.abacus-container {
|
||
flex: 1;
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
margin: 10px 0;
|
||
max-width: 100%;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.abacus-container svg {
|
||
max-width: 100%;
|
||
height: auto;
|
||
}
|
||
|
||
.numeral {
|
||
font-size: 48pt;
|
||
font-weight: bold;
|
||
color: #2E86AB;
|
||
opacity: 0;
|
||
transition: opacity 0.3s ease;
|
||
position: absolute;
|
||
top: 50%;
|
||
left: 50%;
|
||
transform: translate(-50%, -50%);
|
||
background: rgba(255,255,255,0.95);
|
||
padding: 15px 25px;
|
||
border-radius: 8px;
|
||
border: 2px solid #ddd;
|
||
box-shadow: 0 4px 8px rgba(0,0,0,0.2);
|
||
z-index: 10;
|
||
}
|
||
|
||
.flashcard:hover .numeral {
|
||
opacity: 1;
|
||
}
|
||
|
||
.card-number {
|
||
position: absolute;
|
||
top: 10px;
|
||
left: 10px;
|
||
font-size: 0.8em;
|
||
color: #999;
|
||
background: rgba(255,255,255,0.8);
|
||
padding: 2px 6px;
|
||
border-radius: 4px;
|
||
}
|
||
|
||
.instructions {
|
||
background: linear-gradient(135deg, #f8fafc 0%, #e2e8f0 100%);
|
||
padding: 40px;
|
||
border-radius: 20px;
|
||
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
|
||
margin: 50px 0;
|
||
text-align: left;
|
||
position: relative;
|
||
border: 1px solid rgba(102, 126, 234, 0.1);
|
||
}
|
||
|
||
.instructions::before {
|
||
content: '';
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
bottom: 0;
|
||
background: linear-gradient(135deg, rgba(102, 126, 234, 0.03) 0%, rgba(118, 75, 162, 0.03) 100%);
|
||
border-radius: 20px;
|
||
pointer-events: none;
|
||
}
|
||
|
||
.instructions h3 {
|
||
color: #2d3748;
|
||
margin-top: 0;
|
||
margin-bottom: 20px;
|
||
font-size: 1.5rem;
|
||
font-weight: 600;
|
||
position: relative;
|
||
z-index: 1;
|
||
}
|
||
|
||
.instructions h4 {
|
||
color: #4a5568;
|
||
margin-top: 30px;
|
||
margin-bottom: 15px;
|
||
font-size: 1.2rem;
|
||
font-weight: 600;
|
||
position: relative;
|
||
z-index: 1;
|
||
}
|
||
|
||
.instructions p {
|
||
position: relative;
|
||
z-index: 1;
|
||
line-height: 1.7;
|
||
color: #4a5568;
|
||
}
|
||
|
||
.instructions ul {
|
||
position: relative;
|
||
z-index: 1;
|
||
margin: 20px 0;
|
||
padding-left: 0;
|
||
list-style: none;
|
||
}
|
||
|
||
.instructions li {
|
||
padding: 12px 0;
|
||
padding-left: 35px;
|
||
position: relative;
|
||
color: #4a5568;
|
||
line-height: 1.6;
|
||
}
|
||
|
||
.instructions li::before {
|
||
content: '✓';
|
||
position: absolute;
|
||
left: 0;
|
||
top: 12px;
|
||
width: 20px;
|
||
height: 20px;
|
||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||
color: white;
|
||
border-radius: 50%;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-size: 12px;
|
||
font-weight: bold;
|
||
}
|
||
|
||
.instructions p {
|
||
position: relative;
|
||
z-index: 1;
|
||
line-height: 1.7;
|
||
color: #4a5568;
|
||
}
|
||
|
||
.stats {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||
gap: 20px;
|
||
margin-top: 40px;
|
||
padding: 0;
|
||
}
|
||
|
||
.stats div {
|
||
background: white;
|
||
padding: 25px;
|
||
border-radius: 15px;
|
||
font-size: 14px;
|
||
border: 1px solid rgba(102, 126, 234, 0.1);
|
||
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.08);
|
||
position: relative;
|
||
overflow: hidden;
|
||
transition: transform 0.3s ease, box-shadow 0.3s ease;
|
||
text-align: left;
|
||
}
|
||
|
||
.stats div::before {
|
||
content: '';
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
width: 4px;
|
||
height: 100%;
|
||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||
}
|
||
|
||
.stats div:hover {
|
||
transform: translateY(-5px);
|
||
box-shadow: 0 20px 40px rgba(102, 126, 234, 0.15);
|
||
}
|
||
|
||
.stats div strong {
|
||
color: #2d3748;
|
||
display: block;
|
||
margin-bottom: 5px;
|
||
font-weight: 600;
|
||
}
|
||
|
||
/* Configuration Showcase Styles */
|
||
.config-showcase {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 30px;
|
||
margin-top: 30px;
|
||
}
|
||
|
||
.config-item {
|
||
background: white;
|
||
padding: 25px;
|
||
border-radius: 15px;
|
||
border: 1px solid rgba(102, 126, 234, 0.1);
|
||
position: relative;
|
||
transition: all 0.3s ease;
|
||
}
|
||
|
||
.config-item:hover {
|
||
transform: translateY(-2px);
|
||
box-shadow: 0 10px 30px rgba(102, 126, 234, 0.15);
|
||
}
|
||
|
||
.config-item::before {
|
||
content: '';
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
width: 4px;
|
||
height: 100%;
|
||
background: linear-gradient(135deg, #667eea, #764ba2);
|
||
border-radius: 2px 0 0 2px;
|
||
}
|
||
|
||
.config-label {
|
||
font-size: 16px;
|
||
margin-bottom: 8px;
|
||
color: #2d3748;
|
||
}
|
||
|
||
.config-value {
|
||
font-size: 18px;
|
||
color: #4a5568;
|
||
margin-bottom: 10px;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.config-description {
|
||
font-size: 14px;
|
||
color: #718096;
|
||
line-height: 1.5;
|
||
margin-top: 8px;
|
||
}
|
||
|
||
.color-palette {
|
||
display: flex;
|
||
gap: 12px;
|
||
margin: 12px 0;
|
||
align-items: center;
|
||
}
|
||
|
||
.color-sample {
|
||
width: 24px;
|
||
height: 24px;
|
||
border-radius: 50%;
|
||
border: 2px solid rgba(0,0,0,0.1);
|
||
cursor: pointer;
|
||
transition: transform 0.2s ease;
|
||
}
|
||
|
||
.color-sample:hover {
|
||
transform: scale(1.2);
|
||
}
|
||
|
||
.color-sample.heaven {
|
||
background: #8B4513;
|
||
}
|
||
|
||
.color-sample.earth {
|
||
background: #F4A460;
|
||
}
|
||
|
||
.color-sample.frame {
|
||
background: #2F4F4F;
|
||
}
|
||
|
||
.bead-examples {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
}
|
||
|
||
.bead-sample {
|
||
width: 20px;
|
||
height: 20px;
|
||
border: 2px solid rgba(0,0,0,0.2);
|
||
transition: transform 0.2s ease;
|
||
}
|
||
|
||
.bead-sample:hover {
|
||
transform: scale(1.3);
|
||
}
|
||
|
||
.bead-sample.circle {
|
||
border-radius: 50%;
|
||
background: #8B4513;
|
||
}
|
||
|
||
.bead-sample.diamond {
|
||
transform: rotate(45deg);
|
||
background: #8B4513;
|
||
}
|
||
|
||
.bead-sample.square {
|
||
border-radius: 2px;
|
||
background: #8B4513;
|
||
}
|
||
|
||
.shape-label {
|
||
font-size: 13px;
|
||
color: #718096;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.offline-info {
|
||
background: linear-gradient(135deg, #e6fffa 0%, #b2f5ea 100%);
|
||
padding: 30px;
|
||
border-radius: 20px;
|
||
border: 1px solid #4fd1c7;
|
||
margin-top: 40px;
|
||
text-align: center;
|
||
}
|
||
|
||
.offline-info h3 {
|
||
color: #234e52;
|
||
margin-top: 0;
|
||
margin-bottom: 15px;
|
||
font-size: 1.4rem;
|
||
}
|
||
|
||
.offline-info p {
|
||
color: #2d5a5f;
|
||
line-height: 1.6;
|
||
margin-bottom: 20px;
|
||
}
|
||
|
||
.tech-features {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||
gap: 15px;
|
||
margin-top: 20px;
|
||
}
|
||
|
||
.feature {
|
||
background: rgba(255, 255, 255, 0.7);
|
||
padding: 12px 20px;
|
||
border-radius: 25px;
|
||
font-size: 14px;
|
||
font-weight: 500;
|
||
color: #234e52;
|
||
border: 1px solid rgba(79, 209, 199, 0.3);
|
||
}
|
||
|
||
/* Tutorial Styles */
|
||
.tutorial-content {
|
||
max-width: 1000px;
|
||
margin: 0 auto;
|
||
}
|
||
|
||
.tutorial-step {
|
||
background: white;
|
||
margin-bottom: 40px;
|
||
border-radius: 20px;
|
||
overflow: hidden;
|
||
box-shadow: 0 15px 35px rgba(0,0,0,0.08);
|
||
border: 1px solid rgba(102, 126, 234, 0.1);
|
||
}
|
||
|
||
.step-header {
|
||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||
padding: 25px 40px;
|
||
color: white;
|
||
}
|
||
|
||
.step-header h3 {
|
||
margin: 0;
|
||
font-size: 1.5rem;
|
||
font-weight: 600;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 15px;
|
||
}
|
||
|
||
.step-number {
|
||
background: rgba(255,255,255,0.2);
|
||
width: 35px;
|
||
height: 35px;
|
||
border-radius: 50%;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-weight: 700;
|
||
font-size: 1.1rem;
|
||
}
|
||
|
||
.step-content {
|
||
padding: 40px;
|
||
}
|
||
|
||
.tutorial-grid {
|
||
display: grid;
|
||
grid-template-columns: 1fr 400px;
|
||
gap: 40px;
|
||
align-items: center;
|
||
}
|
||
|
||
.tutorial-text h4 {
|
||
color: #2d3748;
|
||
margin-top: 25px;
|
||
margin-bottom: 15px;
|
||
font-size: 1.1rem;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.tutorial-text ol, .tutorial-text ul {
|
||
color: #4a5568;
|
||
line-height: 1.7;
|
||
}
|
||
|
||
.tutorial-text li {
|
||
margin-bottom: 8px;
|
||
}
|
||
|
||
.tutorial-visual {
|
||
text-align: center;
|
||
}
|
||
|
||
.example-abacus {
|
||
background: #f8fafc;
|
||
padding: 20px;
|
||
border-radius: 12px;
|
||
margin-bottom: 15px;
|
||
border: 2px solid rgba(102, 126, 234, 0.1);
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
min-height: 120px;
|
||
}
|
||
|
||
.example-abacus svg {
|
||
max-width: 100%;
|
||
height: auto;
|
||
}
|
||
|
||
.example-label {
|
||
font-weight: 600;
|
||
color: #2d3748;
|
||
font-size: 0.95rem;
|
||
}
|
||
|
||
.example-breakdown {
|
||
font-size: 0.85rem;
|
||
color: #718096;
|
||
margin-top: 5px;
|
||
font-style: italic;
|
||
}
|
||
|
||
.examples-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
|
||
gap: 25px;
|
||
margin-top: 30px;
|
||
}
|
||
|
||
.example-item {
|
||
text-align: center;
|
||
}
|
||
|
||
.example-item .example-abacus {
|
||
margin-bottom: 12px;
|
||
min-height: 100px;
|
||
padding: 15px;
|
||
}
|
||
|
||
.practice-hint {
|
||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||
color: white;
|
||
padding: 10px 15px;
|
||
border-radius: 8px;
|
||
font-size: 0.9rem;
|
||
margin-top: 10px;
|
||
font-style: italic;
|
||
}
|
||
|
||
.reveal-btn {
|
||
background: linear-gradient(135deg, #28a745 0%, #20c997 100%);
|
||
color: white;
|
||
border: none;
|
||
padding: 12px 24px;
|
||
border-radius: 8px;
|
||
font-size: 0.9rem;
|
||
font-weight: 600;
|
||
cursor: pointer;
|
||
margin-top: 15px;
|
||
transition: all 0.3s ease;
|
||
box-shadow: 0 4px 15px rgba(40, 167, 69, 0.3);
|
||
}
|
||
|
||
.reveal-btn:hover {
|
||
transform: translateY(-2px);
|
||
box-shadow: 0 8px 25px rgba(40, 167, 69, 0.4);
|
||
}
|
||
|
||
.reveal-btn:active {
|
||
transform: translateY(0);
|
||
}
|
||
|
||
.revealed {
|
||
animation: revealAnimation 0.5s ease-out;
|
||
}
|
||
|
||
@keyframes revealAnimation {
|
||
0% {
|
||
background: #fff3cd;
|
||
transform: scale(1.05);
|
||
}
|
||
100% {
|
||
background: #f8fafc;
|
||
transform: scale(1);
|
||
}
|
||
}
|
||
|
||
/* Arithmetic Section Styles */
|
||
.arithmetic-intro {
|
||
background: rgba(102, 126, 234, 0.05);
|
||
padding: 20px;
|
||
border-radius: 12px;
|
||
margin-bottom: 30px;
|
||
border-left: 4px solid #667eea;
|
||
}
|
||
|
||
.operation-section {
|
||
margin: 30px 0;
|
||
padding: 25px;
|
||
background: #f8fafc;
|
||
border-radius: 15px;
|
||
border: 1px solid rgba(102, 126, 234, 0.1);
|
||
}
|
||
|
||
.operation-section h4 {
|
||
color: #2d3748;
|
||
margin-top: 0;
|
||
margin-bottom: 20px;
|
||
font-size: 1.3rem;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.calculation-example {
|
||
margin: 20px 0;
|
||
}
|
||
|
||
.calc-step {
|
||
display: grid;
|
||
grid-template-columns: 1fr 2fr;
|
||
gap: 30px;
|
||
align-items: start;
|
||
}
|
||
|
||
.step-description {
|
||
padding: 20px;
|
||
background: white;
|
||
border-radius: 12px;
|
||
box-shadow: 0 4px 12px rgba(0,0,0,0.08);
|
||
}
|
||
|
||
.step-description h5 {
|
||
color: #667eea;
|
||
margin-top: 0;
|
||
margin-bottom: 15px;
|
||
font-size: 1.1rem;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.technique-steps {
|
||
margin: 15px 0;
|
||
padding-left: 20px;
|
||
}
|
||
|
||
.technique-steps li {
|
||
margin-bottom: 8px;
|
||
color: #4a5568;
|
||
line-height: 1.5;
|
||
}
|
||
|
||
.calc-visual-sequence {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 15px;
|
||
padding: 20px;
|
||
background: white;
|
||
border-radius: 12px;
|
||
box-shadow: 0 4px 12px rgba(0,0,0,0.08);
|
||
overflow-x: auto;
|
||
}
|
||
|
||
.calc-frame {
|
||
text-align: center;
|
||
min-width: 120px;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.frame-label {
|
||
font-size: 0.9rem;
|
||
font-weight: 600;
|
||
color: #2d3748;
|
||
margin-bottom: 10px;
|
||
}
|
||
|
||
.calc-arrow {
|
||
font-size: 0.85rem;
|
||
color: #667eea;
|
||
font-weight: 600;
|
||
text-align: center;
|
||
min-width: 80px;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.technique-box {
|
||
background: linear-gradient(135deg, #e3f2fd 0%, #f3e5f5 100%);
|
||
padding: 20px;
|
||
border-radius: 12px;
|
||
margin: 20px 0;
|
||
border: 1px solid rgba(102, 126, 234, 0.2);
|
||
}
|
||
|
||
.technique-box h5 {
|
||
color: #2d3748;
|
||
margin-top: 0;
|
||
margin-bottom: 15px;
|
||
font-size: 1.1rem;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.complement-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||
gap: 12px;
|
||
margin: 15px 0;
|
||
}
|
||
|
||
.complement-pair {
|
||
background: white;
|
||
padding: 12px 15px;
|
||
border-radius: 8px;
|
||
font-family: 'Courier New', monospace;
|
||
font-size: 0.9rem;
|
||
color: #4a5568;
|
||
border: 1px solid rgba(102, 126, 234, 0.15);
|
||
}
|
||
|
||
.mult-layout, .div-layout {
|
||
display: grid;
|
||
grid-template-columns: 1fr 1fr;
|
||
gap: 30px;
|
||
padding: 20px;
|
||
background: white;
|
||
border-radius: 12px;
|
||
box-shadow: 0 4px 12px rgba(0,0,0,0.08);
|
||
}
|
||
|
||
.mult-setup, .mult-result, .div-setup, .div-result {
|
||
text-align: center;
|
||
}
|
||
|
||
.mult-label, .div-label {
|
||
font-size: 0.95rem;
|
||
font-weight: 600;
|
||
color: #2d3748;
|
||
margin-bottom: 15px;
|
||
}
|
||
|
||
.tips-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
|
||
gap: 25px;
|
||
margin-top: 30px;
|
||
}
|
||
|
||
.tip-card {
|
||
background: linear-gradient(135deg, #fff5f5 0%, #f0fff4 100%);
|
||
padding: 25px;
|
||
border-radius: 15px;
|
||
border: 1px solid rgba(102, 126, 234, 0.1);
|
||
box-shadow: 0 8px 20px rgba(0,0,0,0.08);
|
||
transition: transform 0.3s ease, box-shadow 0.3s ease;
|
||
}
|
||
|
||
.tip-card:hover {
|
||
transform: translateY(-5px);
|
||
box-shadow: 0 15px 30px rgba(102, 126, 234, 0.15);
|
||
}
|
||
|
||
.tip-card h4 {
|
||
color: #2d3748;
|
||
margin-top: 0;
|
||
margin-bottom: 15px;
|
||
font-size: 1.1rem;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.tip-card p {
|
||
color: #4a5568;
|
||
line-height: 1.6;
|
||
margin: 0;
|
||
}
|
||
|
||
/* Guide Navigation Styles */
|
||
.guide-nav {
|
||
background: rgba(102, 126, 234, 0.05);
|
||
padding: 20px;
|
||
border-radius: 15px;
|
||
margin-bottom: 40px;
|
||
border: 1px solid rgba(102, 126, 234, 0.1);
|
||
}
|
||
|
||
.guide-nav-buttons {
|
||
display: flex;
|
||
gap: 15px;
|
||
justify-content: center;
|
||
flex-wrap: wrap;
|
||
}
|
||
|
||
.guide-nav-btn {
|
||
padding: 12px 24px;
|
||
border: 2px solid #667eea;
|
||
background: white;
|
||
color: #667eea;
|
||
border-radius: 25px;
|
||
cursor: pointer;
|
||
font-size: 15px;
|
||
font-weight: 600;
|
||
transition: all 0.3s ease;
|
||
text-decoration: none;
|
||
}
|
||
|
||
.guide-nav-btn:hover {
|
||
background: #667eea;
|
||
color: white;
|
||
transform: translateY(-2px);
|
||
box-shadow: 0 8px 20px rgba(102, 126, 234, 0.3);
|
||
}
|
||
|
||
.guide-nav-btn.active {
|
||
background: #667eea;
|
||
color: white;
|
||
box-shadow: 0 6px 15px rgba(102, 126, 234, 0.4);
|
||
}
|
||
|
||
.guide-content {
|
||
position: relative;
|
||
}
|
||
|
||
.guide-section {
|
||
display: none;
|
||
animation: fadeInGuide 0.4s ease-out;
|
||
}
|
||
|
||
.guide-section.active {
|
||
display: block;
|
||
}
|
||
|
||
@keyframes fadeInGuide {
|
||
from {
|
||
opacity: 0;
|
||
transform: translateX(20px);
|
||
}
|
||
to {
|
||
opacity: 1;
|
||
transform: translateX(0);
|
||
}
|
||
}
|
||
|
||
.section-intro {
|
||
text-align: center;
|
||
margin-bottom: 40px;
|
||
padding: 30px;
|
||
background: linear-gradient(135deg, #f8fafc 0%, #e2e8f0 100%);
|
||
border-radius: 15px;
|
||
border: 1px solid rgba(102, 126, 234, 0.1);
|
||
}
|
||
|
||
.section-intro h3 {
|
||
color: #2d3748;
|
||
margin: 0 0 15px 0;
|
||
font-size: 1.8rem;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.section-intro p {
|
||
color: #4a5568;
|
||
margin: 0;
|
||
font-size: 1.1rem;
|
||
line-height: 1.6;
|
||
}
|
||
|
||
@media (max-width: 768px) {
|
||
.tutorial-grid {
|
||
grid-template-columns: 1fr;
|
||
gap: 25px;
|
||
}
|
||
|
||
.step-content {
|
||
padding: 25px;
|
||
}
|
||
|
||
.step-header {
|
||
padding: 20px 25px;
|
||
}
|
||
|
||
.examples-grid {
|
||
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
|
||
gap: 20px;
|
||
}
|
||
|
||
.calc-step {
|
||
grid-template-columns: 1fr;
|
||
gap: 20px;
|
||
}
|
||
|
||
.calc-visual-sequence {
|
||
padding: 15px;
|
||
}
|
||
|
||
.mult-layout, .div-layout {
|
||
grid-template-columns: 1fr;
|
||
gap: 20px;
|
||
}
|
||
|
||
.tips-grid {
|
||
grid-template-columns: 1fr;
|
||
}
|
||
}
|
||
|
||
@media (max-width: 768px) {
|
||
.cards-grid {
|
||
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
|
||
gap: 15px;
|
||
}
|
||
|
||
.flashcard {
|
||
min-height: 120px;
|
||
padding: 15px;
|
||
}
|
||
|
||
.numeral {
|
||
font-size: calc(48pt * 0.8);
|
||
padding: 10px 20px;
|
||
}
|
||
}
|
||
|
||
/* Quiz Styling */
|
||
.quiz-section {
|
||
background: #f8f9fa;
|
||
border-radius: 12px;
|
||
padding: 30px;
|
||
margin: 30px 0;
|
||
text-align: center;
|
||
}
|
||
|
||
.quiz-section h2 {
|
||
color: #333;
|
||
margin-bottom: 10px;
|
||
}
|
||
|
||
.quiz-controls {
|
||
max-width: 600px;
|
||
margin: 0 auto;
|
||
}
|
||
|
||
.control-group {
|
||
margin: 20px 0;
|
||
}
|
||
|
||
.control-group label {
|
||
display: block;
|
||
font-weight: bold;
|
||
margin-bottom: 10px;
|
||
color: #555;
|
||
}
|
||
|
||
.count-buttons {
|
||
display: flex;
|
||
gap: 10px;
|
||
justify-content: center;
|
||
flex-wrap: wrap;
|
||
}
|
||
|
||
.count-btn {
|
||
background: white;
|
||
border: 2px solid #ddd;
|
||
border-radius: 8px;
|
||
padding: 10px 20px;
|
||
cursor: pointer;
|
||
transition: all 0.2s ease;
|
||
font-size: 16px;
|
||
}
|
||
|
||
.count-btn:hover {
|
||
border-color: #4a90e2;
|
||
background: #f0f7ff;
|
||
}
|
||
|
||
.count-btn.active {
|
||
background: #4a90e2;
|
||
color: white;
|
||
border-color: #4a90e2;
|
||
}
|
||
|
||
.slider-container {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
gap: 15px;
|
||
}
|
||
|
||
.slider-container input[type="range"] {
|
||
width: 200px;
|
||
height: 6px;
|
||
border-radius: 3px;
|
||
background: #ddd;
|
||
outline: none;
|
||
-webkit-appearance: none;
|
||
}
|
||
|
||
.slider-container input[type="range"]::-webkit-slider-thumb {
|
||
-webkit-appearance: none;
|
||
appearance: none;
|
||
width: 20px;
|
||
height: 20px;
|
||
border-radius: 50%;
|
||
background: #4a90e2;
|
||
cursor: pointer;
|
||
}
|
||
|
||
.slider-value {
|
||
font-weight: bold;
|
||
color: #4a90e2;
|
||
min-width: 50px;
|
||
}
|
||
|
||
.quiz-start-btn {
|
||
background: #28a745;
|
||
color: white;
|
||
border: none;
|
||
border-radius: 8px;
|
||
padding: 15px 30px;
|
||
font-size: 18px;
|
||
font-weight: bold;
|
||
cursor: pointer;
|
||
transition: background 0.2s ease;
|
||
margin-top: 20px;
|
||
}
|
||
|
||
.quiz-start-btn:hover {
|
||
background: #218838;
|
||
}
|
||
|
||
/* Quiz Game Area */
|
||
.quiz-game {
|
||
text-align: center;
|
||
padding: 40px 20px;
|
||
}
|
||
|
||
.quiz-progress {
|
||
margin-bottom: 30px;
|
||
}
|
||
|
||
.progress-bar {
|
||
width: 100%;
|
||
max-width: 400px;
|
||
height: 8px;
|
||
background: #ddd;
|
||
border-radius: 4px;
|
||
margin: 0 auto 10px;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.progress-fill {
|
||
height: 100%;
|
||
background: #4a90e2;
|
||
transition: width 0.3s ease;
|
||
width: 0%;
|
||
}
|
||
|
||
.progress-text {
|
||
color: #666;
|
||
font-size: 16px;
|
||
}
|
||
|
||
.quiz-display {
|
||
position: relative;
|
||
min-height: 300px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
.quiz-flashcard {
|
||
background: white;
|
||
border-radius: 12px;
|
||
padding: 20px;
|
||
box-shadow: 0 8px 16px rgba(0,0,0,0.1);
|
||
width: min(85vw, 700px);
|
||
height: min(50vh, 400px);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
margin: 0 auto;
|
||
transition: transform 0.3s ease;
|
||
}
|
||
|
||
/* Ensure quiz game section fits in viewport */
|
||
#quiz-game {
|
||
min-height: 100vh;
|
||
display: flex;
|
||
flex-direction: column;
|
||
justify-content: center;
|
||
align-items: center;
|
||
padding: 20px;
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
/* Responsive adjustments for smaller screens */
|
||
@media (max-height: 600px) {
|
||
.quiz-flashcard {
|
||
height: min(40vh, 300px);
|
||
padding: 15px;
|
||
}
|
||
}
|
||
|
||
@media (max-height: 500px) {
|
||
.quiz-flashcard {
|
||
height: min(35vh, 250px);
|
||
padding: 10px;
|
||
}
|
||
|
||
#quiz-game {
|
||
min-height: auto;
|
||
padding: 10px;
|
||
}
|
||
}
|
||
|
||
.quiz-flashcard svg {
|
||
width: 100%;
|
||
height: 100%;
|
||
max-width: 100%;
|
||
max-height: 100%;
|
||
}
|
||
|
||
.quiz-flashcard.pulse {
|
||
transform: scale(1.05);
|
||
}
|
||
|
||
.quiz-flashcard.card-exit-warning {
|
||
border-color: #dc3545;
|
||
box-shadow: 0 0 15px rgba(220, 53, 69, 0.4);
|
||
}
|
||
|
||
.quiz-flashcard.card-fade-out {
|
||
opacity: 0.3;
|
||
transform: scale(0.95);
|
||
transition: all 0.1s ease;
|
||
}
|
||
|
||
.countdown {
|
||
position: absolute;
|
||
top: 50%;
|
||
left: 50%;
|
||
transform: translate(-50%, -50%);
|
||
font-size: 48px;
|
||
font-weight: bold;
|
||
color: #4a90e2;
|
||
text-shadow: 2px 2px 4px rgba(0,0,0,0.1);
|
||
z-index: 10;
|
||
}
|
||
|
||
.countdown.ready {
|
||
color: #28a745;
|
||
}
|
||
|
||
.countdown.go {
|
||
color: #ffc107;
|
||
}
|
||
|
||
.countdown.new-card-flash {
|
||
color: #17a2b8;
|
||
font-size: 24px;
|
||
animation: flashIn 0.15s ease;
|
||
}
|
||
|
||
@keyframes flashIn {
|
||
from {
|
||
opacity: 0;
|
||
transform: translate(-50%, -50%) scale(0.8);
|
||
}
|
||
to {
|
||
opacity: 1;
|
||
transform: translate(-50%, -50%) scale(1);
|
||
}
|
||
}
|
||
|
||
/* Quiz Input */
|
||
.quiz-input {
|
||
text-align: center;
|
||
padding: 40px 20px;
|
||
max-width: 700px;
|
||
margin: 0 auto;
|
||
}
|
||
|
||
.quiz-stats {
|
||
display: flex;
|
||
justify-content: center;
|
||
gap: 30px;
|
||
margin-bottom: 30px;
|
||
padding: 20px;
|
||
background: #f8f9fa;
|
||
border-radius: 12px;
|
||
}
|
||
|
||
.stats-item {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
gap: 5px;
|
||
}
|
||
|
||
.stats-label {
|
||
font-size: 14px;
|
||
color: #666;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.stats-item span:last-child {
|
||
font-size: 24px;
|
||
font-weight: bold;
|
||
color: #2c5f76;
|
||
}
|
||
|
||
.smart-input-container {
|
||
position: relative;
|
||
margin: 40px 0;
|
||
text-align: center;
|
||
}
|
||
|
||
.smart-input-prompt {
|
||
font-size: 16px;
|
||
color: #7a8695;
|
||
margin-bottom: 15px;
|
||
font-weight: normal;
|
||
}
|
||
|
||
.number-display {
|
||
min-height: 60px;
|
||
padding: 20px;
|
||
font-size: 32px;
|
||
font-family: 'Courier New', 'Monaco', monospace;
|
||
text-align: center;
|
||
font-weight: bold;
|
||
color: #2c3e50;
|
||
letter-spacing: 3px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
transition: all 0.3s ease;
|
||
}
|
||
|
||
.current-typing {
|
||
display: inline-block;
|
||
transition: all 0.3s ease;
|
||
}
|
||
|
||
.number-display.correct .current-typing {
|
||
color: #28a745;
|
||
animation: successPulse 0.5s ease;
|
||
}
|
||
|
||
.number-display.incorrect .current-typing {
|
||
color: #dc3545;
|
||
animation: errorShake 0.5s ease;
|
||
}
|
||
|
||
@keyframes successPulse {
|
||
0%, 100% { transform: scale(1); }
|
||
50% { transform: scale(1.05); }
|
||
}
|
||
|
||
@keyframes errorShake {
|
||
0%, 100% { transform: translateX(0); }
|
||
25% { transform: translateX(-10px); }
|
||
75% { transform: translateX(10px); }
|
||
}
|
||
|
||
/* Particle Effects System */
|
||
.particle-container {
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
width: 100vw;
|
||
height: 100vh;
|
||
pointer-events: none;
|
||
z-index: 10000;
|
||
}
|
||
|
||
.particle {
|
||
position: absolute;
|
||
border-radius: 50%;
|
||
pointer-events: none;
|
||
}
|
||
|
||
.particle.star {
|
||
background: linear-gradient(45deg, #ffd700, #ffed4e);
|
||
box-shadow: 0 0 10px #ffd700;
|
||
}
|
||
|
||
.particle.heart {
|
||
background: #ff6b6b;
|
||
border-radius: 0;
|
||
transform: rotate(-45deg);
|
||
}
|
||
|
||
.particle.heart::before,
|
||
.particle.heart::after {
|
||
content: '';
|
||
position: absolute;
|
||
width: 100%;
|
||
height: 100%;
|
||
background: #ff6b6b;
|
||
border-radius: 50%;
|
||
}
|
||
|
||
.particle.heart::before {
|
||
top: -50%;
|
||
left: 0;
|
||
}
|
||
|
||
.particle.heart::after {
|
||
top: 0;
|
||
left: 50%;
|
||
}
|
||
|
||
.particle.circle {
|
||
background: linear-gradient(45deg, #667eea, #764ba2);
|
||
box-shadow: 0 0 8px rgba(102, 126, 234, 0.4);
|
||
}
|
||
|
||
.particle.diamond {
|
||
background: linear-gradient(45deg, #00b894, #00cec9);
|
||
transform: rotate(45deg);
|
||
border-radius: 0;
|
||
}
|
||
|
||
.particle.confetti {
|
||
background: linear-gradient(45deg, #ff6b6b, #feca57);
|
||
border-radius: 2px;
|
||
}
|
||
|
||
/* Burst Animation */
|
||
@keyframes particleBurst {
|
||
0% {
|
||
opacity: 1;
|
||
transform: translateY(0) scale(0) rotate(0deg);
|
||
}
|
||
30% {
|
||
opacity: 1;
|
||
transform: translateY(-20px) scale(1.2) rotate(180deg);
|
||
}
|
||
100% {
|
||
opacity: 0;
|
||
transform: translateY(-80px) scale(0.3) rotate(360deg);
|
||
}
|
||
}
|
||
|
||
/* Celebration Firework */
|
||
@keyframes firework {
|
||
0% {
|
||
opacity: 1;
|
||
transform: translateY(0) scale(0);
|
||
}
|
||
20% {
|
||
opacity: 1;
|
||
transform: translateY(-30px) scale(1.5);
|
||
}
|
||
100% {
|
||
opacity: 0;
|
||
transform: translateY(-120px) scale(0.5);
|
||
}
|
||
}
|
||
|
||
/* Floating Animation */
|
||
@keyframes float {
|
||
0% {
|
||
opacity: 0;
|
||
transform: translateY(20px) scale(0) rotate(0deg);
|
||
}
|
||
20% {
|
||
opacity: 1;
|
||
transform: translateY(0) scale(1) rotate(72deg);
|
||
}
|
||
100% {
|
||
opacity: 0;
|
||
transform: translateY(-100px) scale(0.8) rotate(360deg);
|
||
}
|
||
}
|
||
|
||
/* Trail Effect */
|
||
@keyframes trail {
|
||
0% {
|
||
opacity: 1;
|
||
transform: translate(0, 0) scale(1);
|
||
}
|
||
100% {
|
||
opacity: 0;
|
||
transform: translate(var(--dx), var(--dy)) scale(0.3);
|
||
}
|
||
}
|
||
|
||
/* Sparkle Effect */
|
||
@keyframes sparkle {
|
||
0%, 100% {
|
||
opacity: 0;
|
||
transform: scale(0) rotate(0deg);
|
||
}
|
||
50% {
|
||
opacity: 1;
|
||
transform: scale(1) rotate(180deg);
|
||
}
|
||
}
|
||
|
||
.particle.burst {
|
||
animation: particleBurst 0.8s ease-out forwards;
|
||
}
|
||
|
||
.particle.firework {
|
||
animation: firework 1.2s ease-out forwards;
|
||
}
|
||
|
||
.particle.floating {
|
||
animation: float 2s ease-out forwards;
|
||
}
|
||
|
||
.particle.trail {
|
||
animation: trail 1.5s ease-out forwards;
|
||
}
|
||
|
||
.particle.sparkle {
|
||
animation: sparkle 1s ease-in-out forwards;
|
||
}
|
||
|
||
.input-feedback {
|
||
margin-top: 10px;
|
||
font-weight: bold;
|
||
min-height: 20px;
|
||
}
|
||
|
||
.input-feedback.success {
|
||
color: #28a745;
|
||
}
|
||
|
||
.input-feedback.error {
|
||
color: #dc3545;
|
||
}
|
||
|
||
.input-feedback.survival {
|
||
color: #ff6b6b;
|
||
background: linear-gradient(135deg, rgba(255, 107, 107, 0.1), rgba(255, 193, 7, 0.1));
|
||
padding: 8px 12px;
|
||
border-radius: 8px;
|
||
font-weight: 600;
|
||
border: 1px solid rgba(255, 107, 107, 0.3);
|
||
}
|
||
|
||
.found-numbers {
|
||
margin: 30px 0;
|
||
min-height: 60px;
|
||
}
|
||
|
||
.found-number {
|
||
display: inline-block;
|
||
margin: 5px;
|
||
padding: 10px 15px;
|
||
background: #28a745;
|
||
color: white;
|
||
border-radius: 8px;
|
||
font-weight: bold;
|
||
font-size: 18px;
|
||
animation: slideIn 0.3s ease;
|
||
}
|
||
|
||
@keyframes slideIn {
|
||
from {
|
||
opacity: 0;
|
||
transform: translateY(-20px);
|
||
}
|
||
to {
|
||
opacity: 1;
|
||
transform: translateY(0);
|
||
}
|
||
}
|
||
|
||
.finish-btn {
|
||
background: #2c5f76;
|
||
color: white;
|
||
border: none;
|
||
padding: 15px 30px;
|
||
font-size: 18px;
|
||
border-radius: 8px;
|
||
cursor: pointer;
|
||
transition: all 0.3s ease;
|
||
margin-top: 20px;
|
||
}
|
||
|
||
.finish-btn:hover {
|
||
background: #1e4a5c;
|
||
transform: translateY(-2px);
|
||
}
|
||
|
||
.quiz-finish-buttons {
|
||
display: flex;
|
||
gap: 15px;
|
||
justify-content: center;
|
||
margin-top: 20px;
|
||
flex-wrap: wrap;
|
||
}
|
||
|
||
.give-up-btn {
|
||
background: #17a2b8;
|
||
color: white;
|
||
border: none;
|
||
padding: 15px 30px;
|
||
font-size: 18px;
|
||
border-radius: 8px;
|
||
cursor: pointer;
|
||
transition: all 0.3s ease;
|
||
}
|
||
|
||
.give-up-btn:hover {
|
||
background: #138496;
|
||
transform: translateY(-2px);
|
||
}
|
||
|
||
.input-container {
|
||
margin-top: 20px;
|
||
}
|
||
|
||
.quiz-input textarea {
|
||
width: 100%;
|
||
max-width: 400px;
|
||
height: 120px;
|
||
border: 2px solid #ddd;
|
||
border-radius: 8px;
|
||
padding: 15px;
|
||
font-size: 16px;
|
||
resize: vertical;
|
||
font-family: inherit;
|
||
}
|
||
|
||
.quiz-input textarea:focus {
|
||
outline: none;
|
||
border-color: #4a90e2;
|
||
}
|
||
|
||
.quiz-input button {
|
||
background: #4a90e2;
|
||
color: white;
|
||
border: none;
|
||
border-radius: 8px;
|
||
padding: 12px 24px;
|
||
font-size: 16px;
|
||
cursor: pointer;
|
||
margin-top: 15px;
|
||
}
|
||
|
||
.quiz-input button:hover {
|
||
background: #357abd;
|
||
}
|
||
|
||
/* Quiz Results */
|
||
.quiz-results {
|
||
text-align: center;
|
||
padding: 40px 20px;
|
||
max-width: 700px;
|
||
margin: 0 auto;
|
||
}
|
||
|
||
.score-display {
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
gap: 40px;
|
||
margin: 30px 0;
|
||
flex-wrap: wrap;
|
||
}
|
||
|
||
.score-circle {
|
||
width: 120px;
|
||
height: 120px;
|
||
border-radius: 50%;
|
||
background: linear-gradient(135deg, #4a90e2, #357abd);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
color: white;
|
||
font-size: 24px;
|
||
font-weight: bold;
|
||
box-shadow: 0 4px 8px rgba(0,0,0,0.2);
|
||
}
|
||
|
||
.score-details {
|
||
text-align: left;
|
||
}
|
||
|
||
.score-details p {
|
||
margin: 8px 0;
|
||
font-size: 16px;
|
||
}
|
||
|
||
.results-breakdown {
|
||
background: #f8f9fa;
|
||
border-radius: 8px;
|
||
padding: 20px;
|
||
margin: 30px 0;
|
||
text-align: left;
|
||
}
|
||
|
||
.result-item {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
padding: 10px 0;
|
||
border-bottom: 1px solid #eee;
|
||
}
|
||
|
||
.result-item:last-child {
|
||
border-bottom: none;
|
||
}
|
||
|
||
.result-correct {
|
||
color: #28a745;
|
||
font-weight: bold;
|
||
}
|
||
|
||
.result-incorrect {
|
||
color: #dc3545;
|
||
font-weight: bold;
|
||
}
|
||
|
||
.quiz-actions {
|
||
margin-top: 30px;
|
||
display: flex;
|
||
gap: 15px;
|
||
justify-content: center;
|
||
flex-wrap: wrap;
|
||
}
|
||
|
||
.quiz-actions button {
|
||
padding: 12px 24px;
|
||
border: none;
|
||
border-radius: 8px;
|
||
font-size: 16px;
|
||
cursor: pointer;
|
||
transition: background 0.2s ease;
|
||
}
|
||
|
||
#retry-quiz {
|
||
background: #ffc107;
|
||
color: #333;
|
||
}
|
||
|
||
#retry-quiz:hover {
|
||
background: #e0a800;
|
||
}
|
||
|
||
#back-to-cards {
|
||
background: #6c757d;
|
||
color: white;
|
||
}
|
||
|
||
#back-to-cards:hover {
|
||
background: #545b62;
|
||
}
|
||
|
||
/* End Game Button */
|
||
.end-game-btn {
|
||
background: #dc3545;
|
||
color: white;
|
||
border: none;
|
||
border-radius: 8px;
|
||
padding: 8px 16px;
|
||
font-size: 14px;
|
||
cursor: pointer;
|
||
transition: background 0.2s ease;
|
||
}
|
||
|
||
.end-game-btn:hover {
|
||
background: #c82333;
|
||
}
|
||
|
||
/* Quiz Header for Game Mode */
|
||
.quiz-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 20px;
|
||
padding: 15px 20px;
|
||
background: rgba(74, 144, 226, 0.1);
|
||
border-radius: 8px;
|
||
}
|
||
|
||
.quiz-header .quiz-progress {
|
||
flex: 1;
|
||
}
|
||
|
||
@media (max-width: 768px) {
|
||
.quiz-section {
|
||
padding: 20px;
|
||
}
|
||
|
||
.count-buttons {
|
||
gap: 8px;
|
||
}
|
||
|
||
.count-btn {
|
||
padding: 8px 16px;
|
||
font-size: 14px;
|
||
}
|
||
|
||
.score-display {
|
||
flex-direction: column;
|
||
gap: 20px;
|
||
}
|
||
|
||
.score-details {
|
||
text-align: center;
|
||
}
|
||
|
||
.countdown {
|
||
font-size: 36px;
|
||
}
|
||
|
||
.sorting-section {
|
||
padding: 20px;
|
||
}
|
||
|
||
.sorting-grid {
|
||
grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
|
||
gap: 10px;
|
||
padding: 15px;
|
||
}
|
||
|
||
.sort-card {
|
||
padding: 10px;
|
||
}
|
||
|
||
.sort-count-btn {
|
||
padding: 8px 16px;
|
||
font-size: 14px;
|
||
margin: 2px;
|
||
}
|
||
|
||
.sort-start-btn, .sort-check-btn, .sort-reveal-btn, .sort-new-btn {
|
||
padding: 10px 16px;
|
||
font-size: 14px;
|
||
margin: 5px;
|
||
}
|
||
|
||
.sorting-actions {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
justify-content: center;
|
||
}
|
||
|
||
.tag-filter-bar {
|
||
padding: 15px;
|
||
}
|
||
|
||
.filter-title {
|
||
font-size: 1rem;
|
||
margin-bottom: 12px;
|
||
}
|
||
|
||
.tag-filters {
|
||
gap: 8px;
|
||
}
|
||
|
||
.tag-filter {
|
||
font-size: 0.8rem;
|
||
padding: 6px 12px;
|
||
}
|
||
|
||
.challenges-grid {
|
||
grid-template-columns: 1fr;
|
||
gap: 20px;
|
||
margin: 20px 0;
|
||
}
|
||
|
||
.challenge-card {
|
||
min-height: 80px;
|
||
padding: 15px;
|
||
}
|
||
|
||
.challenge-icon {
|
||
width: 40px;
|
||
height: 40px;
|
||
font-size: 24px;
|
||
}
|
||
|
||
.modal-content {
|
||
width: 95%;
|
||
max-height: 95vh;
|
||
}
|
||
|
||
.modal-header {
|
||
padding: 15px 20px;
|
||
}
|
||
|
||
.modal-body {
|
||
padding: 20px;
|
||
}
|
||
|
||
.modal-header h2 {
|
||
font-size: 18px;
|
||
}
|
||
}
|
||
|
||
/* Card Sorting Styling */
|
||
.sorting-section {
|
||
background: #e8f4f8;
|
||
border-radius: 12px;
|
||
padding: 30px;
|
||
margin: 30px 0;
|
||
text-align: center;
|
||
}
|
||
|
||
.sorting-section h2 {
|
||
color: #2c5f76;
|
||
margin-bottom: 10px;
|
||
}
|
||
|
||
.sorting-controls {
|
||
max-width: 600px;
|
||
margin: 0 auto;
|
||
}
|
||
|
||
.sort-count-btn {
|
||
background: #ffffff;
|
||
color: #2c5f76;
|
||
border: 2px solid #2c5f76;
|
||
border-radius: 8px;
|
||
padding: 10px 20px;
|
||
margin: 0 5px;
|
||
cursor: pointer;
|
||
font-size: 16px;
|
||
transition: all 0.3s ease;
|
||
}
|
||
|
||
.sort-count-btn:hover {
|
||
background: #2c5f76;
|
||
color: white;
|
||
transform: translateY(-2px);
|
||
}
|
||
|
||
.sort-count-btn.active {
|
||
background: #2c5f76;
|
||
color: white;
|
||
}
|
||
|
||
.sorting-actions {
|
||
margin-top: 20px;
|
||
}
|
||
|
||
/* Setup modal body for sticky positioning */
|
||
.modal-body {
|
||
position: relative;
|
||
}
|
||
|
||
/* Sticky header within modal body */
|
||
.sorting-header {
|
||
position: sticky;
|
||
top: 0;
|
||
background: #f8f9fa;
|
||
border-bottom: 2px solid #e1e5e9;
|
||
border-radius: 8px 8px 0 0;
|
||
z-index: 50;
|
||
padding: 12px 20px;
|
||
margin: 0 -20px 15px -20px; /* Extend to edges of modal body */
|
||
}
|
||
|
||
.sorting-header-content {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
flex-wrap: wrap;
|
||
gap: 15px;
|
||
}
|
||
|
||
.sorting-status-group {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 5px;
|
||
}
|
||
|
||
.sorting-timer {
|
||
font-size: 18px;
|
||
font-weight: bold;
|
||
color: #2c5f76;
|
||
}
|
||
|
||
.sorting-controls {
|
||
display: flex;
|
||
gap: 10px;
|
||
flex-wrap: wrap;
|
||
}
|
||
|
||
.header-btn {
|
||
padding: 8px 16px;
|
||
border: none;
|
||
border-radius: 6px;
|
||
font-size: 14px;
|
||
cursor: pointer;
|
||
transition: all 0.2s ease;
|
||
}
|
||
|
||
.check-btn {
|
||
background: #28a745;
|
||
color: white;
|
||
}
|
||
|
||
.check-btn:hover {
|
||
background: #218838;
|
||
}
|
||
|
||
.reveal-btn {
|
||
background: #ffc107;
|
||
color: #212529;
|
||
}
|
||
|
||
.reveal-btn:hover {
|
||
background: #e0a800;
|
||
}
|
||
|
||
.new-btn {
|
||
background: #007bff;
|
||
color: white;
|
||
}
|
||
|
||
.new-btn:hover {
|
||
background: #0056b3;
|
||
}
|
||
|
||
.end-btn {
|
||
background: #dc3545;
|
||
color: white;
|
||
}
|
||
|
||
.end-btn:hover {
|
||
background: #c82333;
|
||
}
|
||
|
||
.sorting-game-actions {
|
||
display: none; /* Hide old action buttons */
|
||
}
|
||
|
||
/* Score Modal Styles */
|
||
.score-modal {
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
background: rgba(0, 0, 0, 0.7);
|
||
z-index: 2000;
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
animation: fadeIn 0.3s ease;
|
||
}
|
||
|
||
.score-modal-content {
|
||
background: white;
|
||
border-radius: 12px;
|
||
max-width: 600px;
|
||
width: 90%;
|
||
max-height: 80vh;
|
||
overflow-y: auto;
|
||
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.3);
|
||
animation: slideIn 0.3s ease;
|
||
}
|
||
|
||
.score-modal-header {
|
||
padding: 20px 30px;
|
||
border-bottom: 1px solid #e1e5e9;
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
}
|
||
|
||
.score-modal-header h3 {
|
||
margin: 0;
|
||
color: #333;
|
||
font-size: 24px;
|
||
}
|
||
|
||
.score-modal-close {
|
||
background: none;
|
||
border: none;
|
||
font-size: 32px;
|
||
cursor: pointer;
|
||
color: #666;
|
||
padding: 0;
|
||
width: 40px;
|
||
height: 40px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
border-radius: 20px;
|
||
transition: all 0.2s ease;
|
||
}
|
||
|
||
.score-modal-close:hover {
|
||
background: #f8f9fa;
|
||
color: #333;
|
||
}
|
||
|
||
.score-modal-body {
|
||
padding: 30px;
|
||
}
|
||
|
||
.score-modal-body .feedback-score {
|
||
font-size: 48px;
|
||
font-weight: bold;
|
||
text-align: center;
|
||
margin-bottom: 20px;
|
||
color: #28a745;
|
||
}
|
||
|
||
.score-modal-body .feedback-message {
|
||
font-size: 20px;
|
||
text-align: center;
|
||
margin-bottom: 15px;
|
||
color: #333;
|
||
}
|
||
|
||
.score-modal-body .feedback-time {
|
||
font-size: 18px;
|
||
text-align: center;
|
||
margin-bottom: 25px;
|
||
color: #666;
|
||
font-weight: bold;
|
||
}
|
||
|
||
.score-modal-body .feedback-breakdown {
|
||
background: #f8f9fa;
|
||
border-radius: 8px;
|
||
padding: 20px;
|
||
margin: 20px 0;
|
||
}
|
||
|
||
.score-modal-body .score-component {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
padding: 8px 0;
|
||
border-bottom: 1px solid #e9ecef;
|
||
}
|
||
|
||
.score-modal-body .score-component:last-child {
|
||
border-bottom: none;
|
||
}
|
||
|
||
.score-modal-body .component-label {
|
||
font-weight: bold;
|
||
color: #333;
|
||
}
|
||
|
||
.score-modal-body .component-score {
|
||
font-weight: bold;
|
||
color: #007bff;
|
||
}
|
||
|
||
.score-modal-body .component-weight {
|
||
color: #666;
|
||
font-size: 14px;
|
||
}
|
||
|
||
.score-modal-body .feedback-details {
|
||
background: #e9ecef;
|
||
padding: 15px;
|
||
border-radius: 6px;
|
||
text-align: center;
|
||
font-size: 16px;
|
||
color: #495057;
|
||
}
|
||
|
||
.score-modal-footer {
|
||
padding: 20px 30px;
|
||
border-top: 1px solid #e1e5e9;
|
||
display: flex;
|
||
gap: 15px;
|
||
justify-content: flex-end;
|
||
}
|
||
|
||
.modal-btn {
|
||
padding: 10px 20px;
|
||
border: none;
|
||
border-radius: 6px;
|
||
cursor: pointer;
|
||
font-size: 16px;
|
||
transition: all 0.2s ease;
|
||
}
|
||
|
||
.new-game-btn {
|
||
background: #007bff;
|
||
color: white;
|
||
}
|
||
|
||
.new-game-btn:hover {
|
||
background: #0056b3;
|
||
}
|
||
|
||
.close-btn {
|
||
background: #6c757d;
|
||
color: white;
|
||
}
|
||
|
||
.close-btn:hover {
|
||
background: #545b62;
|
||
}
|
||
|
||
/* Race Results Specific Styles */
|
||
.race-results-summary {
|
||
text-align: center;
|
||
}
|
||
|
||
.race-podium {
|
||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||
color: white;
|
||
border-radius: 15px;
|
||
padding: 30px;
|
||
margin-bottom: 30px;
|
||
box-shadow: 0 10px 25px rgba(102, 126, 234, 0.3);
|
||
}
|
||
|
||
.medal-display {
|
||
margin-bottom: 25px;
|
||
}
|
||
|
||
.medal-icon {
|
||
font-size: 4rem;
|
||
margin-bottom: 10px;
|
||
display: block;
|
||
filter: drop-shadow(0 4px 8px rgba(0,0,0,0.3));
|
||
}
|
||
|
||
.medal-rank {
|
||
font-size: 1.8rem;
|
||
font-weight: 700;
|
||
margin-bottom: 5px;
|
||
text-shadow: 0 2px 4px rgba(0,0,0,0.3);
|
||
}
|
||
|
||
.medal-title {
|
||
font-size: 1.3rem;
|
||
font-weight: 600;
|
||
opacity: 0.9;
|
||
letter-spacing: 1px;
|
||
}
|
||
|
||
.race-performance {
|
||
display: grid;
|
||
grid-template-columns: repeat(3, 1fr);
|
||
gap: 20px;
|
||
margin-top: 25px;
|
||
}
|
||
|
||
.perf-metric {
|
||
background: rgba(255, 255, 255, 0.15);
|
||
border-radius: 10px;
|
||
padding: 15px;
|
||
backdrop-filter: blur(10px);
|
||
}
|
||
|
||
.metric-label {
|
||
display: block;
|
||
font-size: 0.9rem;
|
||
opacity: 0.8;
|
||
margin-bottom: 5px;
|
||
}
|
||
|
||
.metric-value {
|
||
display: block;
|
||
font-size: 1.4rem;
|
||
font-weight: 700;
|
||
text-shadow: 0 1px 3px rgba(0,0,0,0.3);
|
||
}
|
||
|
||
.race-statistics {
|
||
background: #f8f9fa;
|
||
border-radius: 12px;
|
||
padding: 25px;
|
||
margin-bottom: 25px;
|
||
}
|
||
|
||
.stat-row {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
padding: 12px 0;
|
||
border-bottom: 1px solid #e9ecef;
|
||
}
|
||
|
||
.stat-row:last-child {
|
||
border-bottom: none;
|
||
}
|
||
|
||
.stat-label {
|
||
font-weight: 500;
|
||
color: #495057;
|
||
}
|
||
|
||
.stat-value {
|
||
font-weight: 700;
|
||
color: #007bff;
|
||
font-size: 1.1rem;
|
||
}
|
||
|
||
.race-feedback {
|
||
background: linear-gradient(135deg, #e3f2fd, #f3e5f5);
|
||
border-radius: 10px;
|
||
padding: 20px;
|
||
border-left: 4px solid #2196f3;
|
||
}
|
||
|
||
.race-feedback p {
|
||
margin: 0;
|
||
font-size: 1rem;
|
||
line-height: 1.5;
|
||
color: #495057;
|
||
text-align: center;
|
||
}
|
||
|
||
/* Responsive adjustments for race results */
|
||
@media (max-width: 768px) {
|
||
.race-performance {
|
||
grid-template-columns: 1fr;
|
||
gap: 15px;
|
||
}
|
||
|
||
.medal-icon {
|
||
font-size: 3rem;
|
||
}
|
||
|
||
.medal-rank {
|
||
font-size: 1.5rem;
|
||
}
|
||
|
||
.metric-value {
|
||
font-size: 1.2rem;
|
||
}
|
||
}
|
||
|
||
@keyframes fadeIn {
|
||
from { opacity: 0; }
|
||
to { opacity: 1; }
|
||
}
|
||
|
||
@keyframes slideIn {
|
||
from {
|
||
transform: translateY(-50px);
|
||
opacity: 0;
|
||
}
|
||
to {
|
||
transform: translateY(0);
|
||
opacity: 1;
|
||
}
|
||
}
|
||
|
||
.sort-start-btn, .sort-check-btn, .sort-reveal-btn, .sort-new-btn {
|
||
background: #2c5f76;
|
||
color: white;
|
||
border: none;
|
||
border-radius: 8px;
|
||
padding: 12px 24px;
|
||
font-size: 16px;
|
||
cursor: pointer;
|
||
margin: 0 10px;
|
||
transition: all 0.3s ease;
|
||
}
|
||
|
||
.sort-start-btn:hover, .sort-check-btn:hover, .sort-reveal-btn:hover, .sort-new-btn:hover {
|
||
background: #1e4a61;
|
||
transform: translateY(-2px);
|
||
}
|
||
|
||
.sort-check-btn {
|
||
background: #28a745;
|
||
}
|
||
|
||
.sort-check-btn:hover {
|
||
background: #218838;
|
||
}
|
||
|
||
.sort-reveal-btn {
|
||
background: #ffc107;
|
||
color: #333;
|
||
}
|
||
|
||
.sort-reveal-btn:hover {
|
||
background: #e0a800;
|
||
}
|
||
|
||
.sort-new-btn {
|
||
background: #6f42c1;
|
||
}
|
||
|
||
.sort-new-btn:hover {
|
||
background: #5a32a3;
|
||
}
|
||
|
||
.sorting-instructions {
|
||
margin: 20px 0;
|
||
color: #2c5f76;
|
||
}
|
||
|
||
.sorting-progress {
|
||
background: #fff;
|
||
border-radius: 8px;
|
||
padding: 10px 20px;
|
||
margin: 10px 0;
|
||
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
||
}
|
||
|
||
/* Horizontal layout for sorting game */
|
||
.sorting-game-layout {
|
||
display: flex;
|
||
gap: 30px;
|
||
margin: 20px 0;
|
||
}
|
||
|
||
.available-cards-section {
|
||
flex: 1;
|
||
}
|
||
|
||
.sorting-slots-section {
|
||
flex: 2;
|
||
}
|
||
|
||
.available-cards-section h4,
|
||
.sorting-slots-section h4 {
|
||
margin: 0 0 15px 0;
|
||
color: #2c5f76;
|
||
font-size: 18px;
|
||
text-align: center;
|
||
}
|
||
|
||
.sorting-area {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: 10px;
|
||
justify-content: center;
|
||
padding: 15px;
|
||
background: rgba(255,255,255,0.5);
|
||
border-radius: 8px;
|
||
min-height: 120px;
|
||
border: 2px dashed #2c5f76;
|
||
}
|
||
|
||
@media (max-width: 768px) {
|
||
.sorting-game-layout {
|
||
flex-direction: column;
|
||
gap: 20px;
|
||
}
|
||
|
||
.available-cards-section,
|
||
.sorting-slots-section {
|
||
flex: none;
|
||
}
|
||
}
|
||
|
||
.position-slots {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: 8px;
|
||
justify-content: center;
|
||
align-items: center;
|
||
padding: 15px;
|
||
background: rgba(255,255,255,0.7);
|
||
border-radius: 8px;
|
||
border: 2px dashed #2c5f76;
|
||
}
|
||
|
||
.insert-button {
|
||
width: 32px;
|
||
height: 50px;
|
||
background: #2c5f76;
|
||
color: white;
|
||
border: none;
|
||
border-radius: 20px;
|
||
cursor: pointer;
|
||
font-size: 24px;
|
||
font-weight: bold;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
transition: all 0.3s ease;
|
||
opacity: 0.3;
|
||
}
|
||
|
||
.insert-button:hover {
|
||
opacity: 1;
|
||
background: #1976d2;
|
||
transform: scale(1.1);
|
||
}
|
||
|
||
.insert-button.active {
|
||
opacity: 1;
|
||
background: #1976d2;
|
||
box-shadow: 0 4px 12px rgba(25, 118, 210, 0.3);
|
||
}
|
||
|
||
.insert-button.disabled {
|
||
opacity: 0.1;
|
||
cursor: not-allowed;
|
||
}
|
||
|
||
.position-slot {
|
||
width: 90px;
|
||
height: 110px;
|
||
border: 2px solid #2c5f76;
|
||
border-radius: 8px;
|
||
background: #fff;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
cursor: pointer;
|
||
transition: all 0.3s ease;
|
||
position: relative;
|
||
}
|
||
|
||
.position-slot.gradient-bg {
|
||
color: white;
|
||
border-color: rgba(255,255,255,0.3);
|
||
}
|
||
|
||
.position-slot:hover {
|
||
background: #f0f8ff;
|
||
border-color: #1a4a5c;
|
||
transform: scale(1.05);
|
||
}
|
||
|
||
.position-slot.active {
|
||
background: #e3f2fd;
|
||
border-color: #1976d2;
|
||
box-shadow: 0 4px 12px rgba(25, 118, 210, 0.3);
|
||
}
|
||
|
||
|
||
.position-slot .slot-label {
|
||
font-size: 12px;
|
||
text-align: center;
|
||
font-weight: 500;
|
||
text-shadow: 0 1px 2px rgba(0,0,0,0.3);
|
||
/* Color will be set dynamically based on background */
|
||
}
|
||
|
||
.position-slot.filled .slot-label {
|
||
position: absolute;
|
||
bottom: 8px;
|
||
left: 8px;
|
||
right: 8px;
|
||
color: #2c3e50;
|
||
text-shadow: none;
|
||
font-size: 11px;
|
||
font-weight: 600;
|
||
opacity: 0;
|
||
transition: opacity 0.3s ease;
|
||
background: rgba(255, 255, 255, 0.9);
|
||
border-radius: 4px;
|
||
padding: 4px 8px;
|
||
}
|
||
|
||
.position-slot.filled:hover .slot-label {
|
||
opacity: 1;
|
||
}
|
||
|
||
.position-slot.filled {
|
||
background: #e8f5e8;
|
||
border-color: #28a745;
|
||
}
|
||
|
||
.position-slot.filled .slot-card {
|
||
position: absolute;
|
||
top: 5px;
|
||
left: 5px;
|
||
right: 5px;
|
||
bottom: 5px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
.position-slot.correct {
|
||
border-color: #28a745;
|
||
background: #f8fff9;
|
||
}
|
||
|
||
.position-slot.incorrect {
|
||
border-color: #dc3545;
|
||
background: #fff8f8;
|
||
animation: shake 0.5s ease-in-out;
|
||
}
|
||
|
||
.sorting-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
||
gap: 15px;
|
||
margin: 30px 0;
|
||
padding: 20px;
|
||
background: rgba(255,255,255,0.7);
|
||
border-radius: 12px;
|
||
min-height: 300px;
|
||
}
|
||
|
||
.sort-card {
|
||
background: white;
|
||
border-radius: 8px;
|
||
padding: 8px;
|
||
box-shadow: 0 2px 6px rgba(0,0,0,0.1);
|
||
cursor: pointer;
|
||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||
border: 2px solid transparent;
|
||
position: relative;
|
||
user-select: none;
|
||
width: 90px;
|
||
height: 90px;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
.sort-card .card-svg svg {
|
||
max-width: 100%;
|
||
height: auto;
|
||
}
|
||
|
||
.sort-card:hover {
|
||
transform: translateY(-5px);
|
||
box-shadow: 0 8px 16px rgba(0,0,0,0.2);
|
||
border-color: #2c5f76;
|
||
}
|
||
|
||
.sort-card.selected {
|
||
border-color: #1976d2;
|
||
background: #e3f2fd;
|
||
transform: scale(1.1);
|
||
box-shadow: 0 6px 20px rgba(25, 118, 210, 0.3);
|
||
}
|
||
|
||
.sort-card.placed {
|
||
opacity: 0.7;
|
||
transform: scale(0.9);
|
||
cursor: default;
|
||
}
|
||
|
||
.sort-card.placed:hover {
|
||
transform: scale(0.95);
|
||
border-color: #28a745;
|
||
}
|
||
|
||
.sort-card.correct {
|
||
border-color: #28a745;
|
||
background: #f8fff9;
|
||
}
|
||
|
||
.sort-card.incorrect {
|
||
border-color: #dc3545;
|
||
background: #fff8f8;
|
||
animation: shake 0.5s ease-in-out;
|
||
}
|
||
|
||
.sort-card .card-position {
|
||
position: absolute;
|
||
top: 5px;
|
||
left: 5px;
|
||
background: rgba(44, 95, 118, 0.8);
|
||
color: white;
|
||
border-radius: 50%;
|
||
width: 24px;
|
||
height: 24px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-size: 12px;
|
||
font-weight: bold;
|
||
}
|
||
|
||
.sort-card .revealed-number {
|
||
position: absolute;
|
||
top: 5px;
|
||
right: 5px;
|
||
background: #ffc107;
|
||
color: #333;
|
||
border-radius: 4px;
|
||
padding: 2px 8px;
|
||
font-size: 14px;
|
||
font-weight: bold;
|
||
display: none;
|
||
}
|
||
|
||
.sort-card.revealed .revealed-number {
|
||
display: block;
|
||
}
|
||
|
||
.sorting-feedback {
|
||
background: white;
|
||
border-radius: 12px;
|
||
padding: 20px;
|
||
margin: 20px 0;
|
||
text-align: center;
|
||
}
|
||
|
||
.feedback-perfect {
|
||
background: linear-gradient(135deg, #d4edda, #c3e6cb);
|
||
color: #155724;
|
||
border: 2px solid #28a745;
|
||
}
|
||
|
||
.feedback-good {
|
||
background: linear-gradient(135deg, #fff3cd, #ffeaa7);
|
||
color: #856404;
|
||
border: 2px solid #ffc107;
|
||
}
|
||
|
||
.feedback-needs-work {
|
||
background: linear-gradient(135deg, #f8d7da, #f5c6cb);
|
||
color: #721c24;
|
||
border: 2px solid #dc3545;
|
||
}
|
||
|
||
.feedback-score {
|
||
font-size: 24px;
|
||
font-weight: bold;
|
||
margin: 10px 0;
|
||
}
|
||
|
||
.feedback-message {
|
||
font-size: 18px;
|
||
margin: 10px 0;
|
||
}
|
||
|
||
.feedback-details {
|
||
margin-top: 15px;
|
||
font-size: 14px;
|
||
opacity: 0.8;
|
||
}
|
||
|
||
.feedback-breakdown {
|
||
margin: 20px 0;
|
||
padding: 15px;
|
||
background: rgba(255, 255, 255, 0.1);
|
||
border-radius: 8px;
|
||
}
|
||
|
||
.score-component {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin: 8px 0;
|
||
padding: 5px 0;
|
||
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
||
}
|
||
|
||
.score-component:last-child {
|
||
border-bottom: none;
|
||
}
|
||
|
||
.component-label {
|
||
font-weight: bold;
|
||
flex: 1;
|
||
}
|
||
|
||
.component-score {
|
||
font-weight: bold;
|
||
margin: 0 10px;
|
||
min-width: 40px;
|
||
text-align: right;
|
||
}
|
||
|
||
.component-weight {
|
||
font-size: 12px;
|
||
opacity: 0.7;
|
||
min-width: 80px;
|
||
text-align: right;
|
||
}
|
||
|
||
@keyframes shake {
|
||
0%, 100% { transform: translateX(0); }
|
||
25% { transform: translateX(-5px); }
|
||
75% { transform: translateX(5px); }
|
||
}
|
||
|
||
@keyframes success-pulse {
|
||
0% { transform: scale(1); }
|
||
50% { transform: scale(1.05); }
|
||
100% { transform: scale(1); }
|
||
}
|
||
|
||
.sort-card.success {
|
||
animation: success-pulse 0.6s ease-in-out;
|
||
}
|
||
|
||
/* Challenge Buttons */
|
||
.challenges-section {
|
||
margin: 40px 0;
|
||
max-width: 1200px;
|
||
margin-left: auto;
|
||
margin-right: auto;
|
||
}
|
||
|
||
.section-header {
|
||
text-align: center;
|
||
margin-bottom: 30px;
|
||
}
|
||
|
||
.section-header h2 {
|
||
margin: 0 0 10px 0;
|
||
color: #2c3e50;
|
||
font-size: 28px;
|
||
}
|
||
|
||
.section-header p {
|
||
margin: 0;
|
||
color: #7a8695;
|
||
font-size: 16px;
|
||
}
|
||
|
||
/* Tag Filter Bar */
|
||
.tag-filter-bar {
|
||
background: white;
|
||
border-radius: 16px;
|
||
padding: 25px;
|
||
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
|
||
margin-bottom: 30px;
|
||
}
|
||
|
||
.filter-title {
|
||
font-size: 1.1rem;
|
||
font-weight: 600;
|
||
color: #2c3e50;
|
||
margin-bottom: 15px;
|
||
text-align: center;
|
||
}
|
||
|
||
.tag-filters {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: 12px;
|
||
justify-content: center;
|
||
}
|
||
|
||
.tag-filter {
|
||
background: #f8f9fa;
|
||
border: 2px solid #e9ecef;
|
||
border-radius: 25px;
|
||
padding: 8px 16px;
|
||
font-size: 0.9rem;
|
||
font-weight: 500;
|
||
color: #6c757d;
|
||
cursor: pointer;
|
||
transition: all 0.3s ease;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 5px;
|
||
}
|
||
|
||
.tag-filter:hover {
|
||
background: #e9ecef;
|
||
transform: translateY(-2px);
|
||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||
}
|
||
|
||
.tag-filter.active {
|
||
background: linear-gradient(135deg, #4a90e2, #357abd);
|
||
color: white;
|
||
border-color: #4a90e2;
|
||
box-shadow: 0 4px 15px rgba(74, 144, 226, 0.3);
|
||
}
|
||
|
||
.tag-count {
|
||
background: rgba(255, 255, 255, 0.2);
|
||
border-radius: 12px;
|
||
padding: 2px 8px;
|
||
font-size: 0.8rem;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.tag-filter.active .tag-count {
|
||
background: rgba(255, 255, 255, 0.3);
|
||
}
|
||
|
||
.challenges-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fit, minmax(350px, 1fr));
|
||
gap: 30px;
|
||
margin-top: 0px;
|
||
}
|
||
|
||
.challenge-category {
|
||
background: white;
|
||
border-radius: 16px;
|
||
padding: 25px;
|
||
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
|
||
border: 1px solid #f0f2f5;
|
||
}
|
||
|
||
.category-title {
|
||
margin: 0 0 20px 0;
|
||
color: #2c3e50;
|
||
font-size: 18px;
|
||
font-weight: 600;
|
||
text-align: center;
|
||
padding-bottom: 15px;
|
||
border-bottom: 2px solid #f0f2f5;
|
||
}
|
||
|
||
.challenge-cards {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 15px;
|
||
}
|
||
|
||
.challenge-card {
|
||
background: linear-gradient(135deg, #4a90e2, #357abd);
|
||
color: white;
|
||
border: none;
|
||
border-radius: 12px;
|
||
padding: 20px;
|
||
cursor: pointer;
|
||
transition: all 0.3s ease;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 15px;
|
||
text-align: left;
|
||
min-height: 100px;
|
||
}
|
||
|
||
.challenge-card:hover {
|
||
transform: translateY(-2px);
|
||
box-shadow: 0 8px 25px rgba(74, 144, 226, 0.4);
|
||
}
|
||
|
||
.challenge-icon {
|
||
font-size: 32px;
|
||
flex-shrink: 0;
|
||
width: 50px;
|
||
height: 50px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
background: rgba(255,255,255,0.2);
|
||
border-radius: 12px;
|
||
}
|
||
|
||
.challenge-content {
|
||
flex: 1;
|
||
}
|
||
|
||
.challenge-content h4 {
|
||
margin: 0 0 8px 0;
|
||
font-size: 18px;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.challenge-content p {
|
||
margin: 0 0 12px 0;
|
||
opacity: 0.9;
|
||
font-size: 14px;
|
||
line-height: 1.4;
|
||
}
|
||
|
||
.challenge-tags {
|
||
display: flex;
|
||
gap: 10px;
|
||
flex-wrap: wrap;
|
||
margin-top: 8px;
|
||
}
|
||
|
||
.tag {
|
||
background: rgba(255,255,255,0.2);
|
||
padding: 4px 8px;
|
||
border-radius: 6px;
|
||
font-size: 12px;
|
||
font-weight: 500;
|
||
border: 1px solid rgba(255,255,255,0.3);
|
||
}
|
||
|
||
.sorting-card {
|
||
background: linear-gradient(135deg, #2c5f76, #1e4a61);
|
||
}
|
||
|
||
.sorting-card:hover {
|
||
box-shadow: 0 8px 25px rgba(44, 95, 118, 0.4);
|
||
}
|
||
|
||
.matching-card {
|
||
background: linear-gradient(135deg, #7b4397, #dc2430);
|
||
}
|
||
|
||
.matching-card:hover {
|
||
box-shadow: 0 8px 25px rgba(123, 67, 151, 0.4);
|
||
}
|
||
|
||
.start-game-btn {
|
||
background: linear-gradient(135deg, #6c5ce7, #a29bfe);
|
||
color: white;
|
||
border: none;
|
||
padding: 15px 20px;
|
||
border-radius: 12px;
|
||
cursor: pointer;
|
||
transition: all 0.3s ease;
|
||
text-align: center;
|
||
min-height: 70px;
|
||
display: flex;
|
||
flex-direction: column;
|
||
justify-content: center;
|
||
align-items: center;
|
||
gap: 4px;
|
||
}
|
||
|
||
.start-game-btn:hover {
|
||
background: linear-gradient(135deg, #5f4ed6, #9085f5);
|
||
transform: translateY(-2px);
|
||
box-shadow: 0 8px 20px rgba(108, 92, 231, 0.4);
|
||
}
|
||
|
||
.start-game-btn .btn-main {
|
||
font-size: 16px;
|
||
font-weight: 600;
|
||
line-height: 1.2;
|
||
}
|
||
|
||
.start-game-btn .btn-sub {
|
||
font-size: 13px;
|
||
opacity: 0.9;
|
||
font-weight: 400;
|
||
}
|
||
|
||
.mode-buttons, .timer-buttons {
|
||
display: flex;
|
||
gap: 10px;
|
||
flex-wrap: wrap;
|
||
}
|
||
|
||
.mode-btn, .timer-btn, .game-type-btn, .timeout-btn {
|
||
background: #f8f9fa;
|
||
border: 2px solid #e9ecef;
|
||
padding: 10px 15px;
|
||
border-radius: 8px;
|
||
cursor: pointer;
|
||
transition: all 0.3s ease;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
min-width: 120px;
|
||
justify-content: center;
|
||
}
|
||
|
||
.mode-btn.active, .timer-btn.active, .game-type-btn.active, .timeout-btn.active {
|
||
background: #007bff;
|
||
border-color: #007bff;
|
||
color: white;
|
||
}
|
||
|
||
.mode-btn:hover, .timer-btn:hover, .game-type-btn:hover, .timeout-btn:hover {
|
||
border-color: #007bff;
|
||
background: #e3f2fd;
|
||
}
|
||
|
||
.mode-btn.active:hover, .timer-btn.active:hover, .game-type-btn.active:hover, .timeout-btn.active:hover {
|
||
background: #0056b3;
|
||
}
|
||
|
||
.mode-icon {
|
||
font-size: 18px;
|
||
}
|
||
|
||
.mode-text {
|
||
font-weight: 500;
|
||
}
|
||
|
||
.timer-btn {
|
||
min-width: 80px;
|
||
font-size: 14px;
|
||
font-weight: 500;
|
||
}
|
||
|
||
/* Matching Game Styles */
|
||
.matching-header {
|
||
position: sticky;
|
||
top: 0;
|
||
background: #f8f9fa;
|
||
border-bottom: 2px solid #e9ecef;
|
||
padding: 15px 20px;
|
||
z-index: 100;
|
||
margin: 0 -20px 15px -20px;
|
||
}
|
||
|
||
.matching-header-content {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
}
|
||
|
||
.matching-status-group {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 5px;
|
||
}
|
||
|
||
.matching-stats {
|
||
display: flex;
|
||
gap: 15px;
|
||
font-size: 14px;
|
||
color: #6c757d;
|
||
}
|
||
|
||
.matching-timer {
|
||
font-weight: 600;
|
||
color: #495057;
|
||
}
|
||
|
||
.player-stats {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 20px;
|
||
margin-top: 10px;
|
||
}
|
||
|
||
.player-info {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
padding: 8px 12px;
|
||
background: #f8f9fa;
|
||
border-radius: 8px;
|
||
min-width: 80px;
|
||
}
|
||
|
||
.player-info.active {
|
||
color: white;
|
||
}
|
||
|
||
.player-info.player1 {
|
||
border: 2px solid #007bff;
|
||
}
|
||
|
||
.player-info.player1.active {
|
||
background: #007bff;
|
||
}
|
||
|
||
.player-info.player2 {
|
||
border: 2px solid #fd7e14;
|
||
}
|
||
|
||
.player-info.player2.active {
|
||
background: #fd7e14;
|
||
}
|
||
|
||
.player-name {
|
||
font-size: 12px;
|
||
font-weight: 600;
|
||
margin-bottom: 2px;
|
||
}
|
||
|
||
.player-score {
|
||
font-size: 14px;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.current-turn {
|
||
font-size: 16px;
|
||
font-weight: 600;
|
||
color: #007bff;
|
||
text-align: center;
|
||
}
|
||
|
||
.turn-timer {
|
||
font-size: 20px;
|
||
font-weight: 700;
|
||
color: #495057;
|
||
background: #e9ecef;
|
||
padding: 8px 12px;
|
||
border-radius: 8px;
|
||
min-width: 60px;
|
||
text-align: center;
|
||
border: 2px solid #dee2e6;
|
||
}
|
||
|
||
.turn-timer.warning {
|
||
background: #fff3cd;
|
||
color: #856404;
|
||
animation: timerPulse 1s infinite;
|
||
}
|
||
|
||
.turn-timer.critical {
|
||
background: #f8d7da;
|
||
color: #721c24;
|
||
animation: timerPulse 0.5s infinite;
|
||
}
|
||
|
||
.turn-timer.waiting {
|
||
background: #d1ecf1;
|
||
color: #0c5460;
|
||
opacity: 0.8;
|
||
}
|
||
|
||
@keyframes timerPulse {
|
||
0%, 100% { transform: scale(1); }
|
||
50% { transform: scale(1.1); }
|
||
}
|
||
|
||
.two-player-results {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
gap: 30px;
|
||
margin: 20px 0;
|
||
padding: 20px;
|
||
background: #f8f9fa;
|
||
border-radius: 12px;
|
||
}
|
||
|
||
.player-result {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
gap: 10px;
|
||
}
|
||
|
||
.player-name {
|
||
font-size: 16px;
|
||
font-weight: 600;
|
||
color: #495057;
|
||
}
|
||
|
||
.player-final-score {
|
||
font-size: 24px;
|
||
font-weight: 700;
|
||
color: #007bff;
|
||
}
|
||
|
||
.vs-divider {
|
||
font-size: 18px;
|
||
font-weight: 700;
|
||
color: #6c757d;
|
||
padding: 0 10px;
|
||
}
|
||
|
||
.game-summary {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
margin-top: 20px;
|
||
padding: 15px;
|
||
background: #e3f2fd;
|
||
border-radius: 8px;
|
||
}
|
||
|
||
.summary-item {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
gap: 5px;
|
||
}
|
||
|
||
.summary-label {
|
||
font-size: 12px;
|
||
color: #6c757d;
|
||
text-transform: uppercase;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.summary-value {
|
||
font-size: 16px;
|
||
font-weight: 600;
|
||
color: #495057;
|
||
}
|
||
|
||
.matching-grid {
|
||
display: grid;
|
||
gap: 8px;
|
||
padding: 10px;
|
||
justify-content: center;
|
||
width: 100%;
|
||
max-width: 100%;
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
.matching-grid.grid-3x4 {
|
||
grid-template-columns: repeat(4, 1fr);
|
||
aspect-ratio: 4/3;
|
||
width: min(100%, calc(100vh - 280px) * 4/3);
|
||
height: min(calc(100vw - 40px) * 3/4, calc(100vh - 280px));
|
||
}
|
||
|
||
.matching-grid.grid-4x4 {
|
||
grid-template-columns: repeat(4, 1fr);
|
||
aspect-ratio: 1;
|
||
width: min(100%, calc(100vh - 280px));
|
||
height: min(calc(100vw - 40px), calc(100vh - 280px));
|
||
}
|
||
|
||
.matching-grid.grid-4x6 {
|
||
grid-template-columns: repeat(6, 1fr);
|
||
aspect-ratio: 6/4;
|
||
width: min(100%, calc(100vh - 280px) * 6/4);
|
||
height: min(calc(100vw - 40px) * 4/6, calc(100vh - 280px));
|
||
}
|
||
|
||
.matching-grid.grid-5x6 {
|
||
grid-template-columns: repeat(6, 1fr);
|
||
aspect-ratio: 6/5;
|
||
width: min(100%, calc(100vh - 280px) * 6/5);
|
||
height: min(calc(100vw - 40px) * 5/6, calc(100vh - 280px));
|
||
}
|
||
|
||
.match-card {
|
||
aspect-ratio: 1;
|
||
background: #ffffff;
|
||
border: 2px solid #e9ecef;
|
||
border-radius: 8px;
|
||
cursor: pointer;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
position: relative;
|
||
transition: all 0.3s ease;
|
||
user-select: none;
|
||
width: 100%;
|
||
height: 100%;
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
/* Cards automatically size to fill grid cells while maintaining aspect ratio */
|
||
.match-card {
|
||
/* Let the card fill the grid cell */
|
||
width: 100%;
|
||
height: 100%;
|
||
min-width: 0;
|
||
min-height: 0;
|
||
}
|
||
|
||
.match-card:hover {
|
||
border-color: #007bff;
|
||
transform: translateY(-2px);
|
||
box-shadow: 0 4px 12px rgba(0, 123, 255, 0.2);
|
||
}
|
||
|
||
.match-card.flipped {
|
||
background: #f8f9fa;
|
||
border-color: #007bff;
|
||
}
|
||
|
||
.match-card.matched {
|
||
background: #d4edda;
|
||
border-color: #28a745;
|
||
cursor: default;
|
||
position: relative;
|
||
opacity: 0.7;
|
||
filter: grayscale(20%);
|
||
transform: scale(0.95);
|
||
transition: all 0.3s ease;
|
||
}
|
||
|
||
.match-card.matched:hover {
|
||
transform: scale(0.95);
|
||
box-shadow: none;
|
||
border-color: #28a745;
|
||
}
|
||
|
||
.match-card.matched-player1 {
|
||
background: #cce7ff;
|
||
border-color: #007bff;
|
||
box-shadow: none;
|
||
opacity: 0.7;
|
||
filter: grayscale(20%);
|
||
}
|
||
|
||
.match-card.matched-player1:hover {
|
||
border-color: #007bff;
|
||
box-shadow: none;
|
||
}
|
||
|
||
.match-card.matched-player1::before {
|
||
border-color: #007bff !important;
|
||
background: linear-gradient(45deg, rgba(0, 123, 255, 0.1), rgba(0, 123, 255, 0.05)) !important;
|
||
}
|
||
|
||
.match-card.matched-player1::after {
|
||
background: #007bff !important;
|
||
box-shadow: 0 0 0 1px #007bff, 0 0 8px rgba(0, 123, 255, 0.4) !important;
|
||
}
|
||
|
||
.match-card.matched-player2 {
|
||
background: #ffe6cc;
|
||
border-color: #fd7e14;
|
||
box-shadow: none;
|
||
opacity: 0.7;
|
||
filter: grayscale(20%);
|
||
}
|
||
|
||
.match-card.matched-player2:hover {
|
||
border-color: #fd7e14;
|
||
box-shadow: none;
|
||
}
|
||
|
||
.match-card.matched-player2::before {
|
||
border-color: #fd7e14 !important;
|
||
background: linear-gradient(45deg, rgba(253, 126, 20, 0.1), rgba(253, 126, 20, 0.05)) !important;
|
||
}
|
||
|
||
.match-card.matched-player2::after {
|
||
background: #fd7e14 !important;
|
||
box-shadow: 0 0 0 1px #fd7e14, 0 0 8px rgba(253, 126, 20, 0.4) !important;
|
||
}
|
||
|
||
.player-badge {
|
||
position: absolute;
|
||
top: -8px;
|
||
right: -8px;
|
||
width: 24px;
|
||
height: 24px;
|
||
border-radius: 50%;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-size: 12px;
|
||
font-weight: 700;
|
||
color: white;
|
||
border: 2px solid white;
|
||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
|
||
z-index: 20;
|
||
}
|
||
|
||
.player-badge.player1 {
|
||
background: #007bff;
|
||
}
|
||
|
||
.player-badge.player2 {
|
||
background: #fd7e14;
|
||
}
|
||
|
||
.match-card.matched::after {
|
||
content: '';
|
||
position: absolute;
|
||
bottom: 3px;
|
||
left: 3px;
|
||
width: 12px;
|
||
height: 12px;
|
||
background: #28a745;
|
||
border: 2px solid white;
|
||
border-radius: 50%;
|
||
box-shadow: 0 0 0 1px #28a745, 0 0 8px rgba(40, 167, 69, 0.4);
|
||
pointer-events: none;
|
||
z-index: 10;
|
||
}
|
||
|
||
.match-card.matched::before {
|
||
content: '';
|
||
position: absolute;
|
||
top: -2px;
|
||
left: -2px;
|
||
right: -2px;
|
||
bottom: -2px;
|
||
border: 2px solid #28a745;
|
||
border-radius: 10px;
|
||
background: linear-gradient(45deg, rgba(40, 167, 69, 0.1), rgba(40, 167, 69, 0.05));
|
||
pointer-events: none;
|
||
z-index: 1;
|
||
}
|
||
|
||
.match-card.matched .match-card-content {
|
||
opacity: 0.6;
|
||
}
|
||
|
||
.match-card.invalid-move {
|
||
animation: invalidMoveShake 0.6s ease-in-out;
|
||
border-color: #dc3545 !important;
|
||
}
|
||
|
||
@keyframes invalidMoveShake {
|
||
0%, 100% { transform: translateX(0); }
|
||
10%, 30%, 50%, 70%, 90% { transform: translateX(-3px); }
|
||
20%, 40%, 60%, 80% { transform: translateX(3px); }
|
||
}
|
||
|
||
.match-card.valid-choice {
|
||
border-color: #28a745 !important;
|
||
box-shadow: 0 0 8px rgba(40, 167, 69, 0.4);
|
||
animation: validChoicePulse 2s infinite;
|
||
}
|
||
|
||
.match-card.invalid-choice {
|
||
opacity: 0.5;
|
||
cursor: not-allowed;
|
||
}
|
||
|
||
.match-card.invalid-choice:hover {
|
||
transform: none;
|
||
box-shadow: none;
|
||
}
|
||
|
||
@keyframes validChoicePulse {
|
||
0%, 100% { box-shadow: 0 0 8px rgba(40, 167, 69, 0.4); }
|
||
50% { box-shadow: 0 0 12px rgba(40, 167, 69, 0.7); }
|
||
}
|
||
|
||
@keyframes matchedCard {
|
||
0% {
|
||
transform: scale(1) rotate(0deg);
|
||
opacity: 1;
|
||
}
|
||
50% {
|
||
transform: scale(1.1) rotate(2deg);
|
||
opacity: 0.9;
|
||
}
|
||
100% {
|
||
transform: scale(0.95) rotate(0deg);
|
||
opacity: 0.7;
|
||
}
|
||
}
|
||
|
||
.match-card.matched {
|
||
animation: matchedCard 0.6s ease-out forwards;
|
||
}
|
||
|
||
.match-card-content {
|
||
display: none;
|
||
width: 100%;
|
||
height: 100%;
|
||
padding: 8px;
|
||
}
|
||
|
||
.match-card.flipped .match-card-content,
|
||
.match-card.matched .match-card-content {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
.match-card-number {
|
||
font-size: 24px;
|
||
font-weight: bold;
|
||
color: #495057;
|
||
}
|
||
|
||
.match-card-abacus {
|
||
width: 100%;
|
||
height: 100%;
|
||
}
|
||
|
||
.match-card-abacus svg {
|
||
width: 100%;
|
||
height: 100%;
|
||
}
|
||
|
||
.match-card-back {
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
bottom: 0;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
color: white;
|
||
font-size: 14px;
|
||
font-weight: 600;
|
||
gap: 4px;
|
||
border-radius: 6px;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.match-card-back.abacus-type {
|
||
background: linear-gradient(135deg, #6c5ce7, #a29bfe);
|
||
}
|
||
|
||
.match-card-back.number-type {
|
||
background: linear-gradient(135deg, #00b894, #00cec9);
|
||
}
|
||
|
||
.match-card-back.friends-5-type {
|
||
background: linear-gradient(135deg, #ff6b6b, #feca57);
|
||
}
|
||
|
||
.match-card-back.friends-10-type {
|
||
background: linear-gradient(135deg, #667eea, #764ba2);
|
||
}
|
||
|
||
.complement-number {
|
||
font-size: 1.8rem;
|
||
font-weight: bold;
|
||
text-align: center;
|
||
line-height: 1.2;
|
||
white-space: pre-line;
|
||
}
|
||
|
||
.match-card-back .card-type-icon {
|
||
font-size: 40px;
|
||
}
|
||
|
||
.match-card.flipped .match-card-back,
|
||
.match-card.matched .match-card-back {
|
||
display: none;
|
||
}
|
||
|
||
.matching-instructions {
|
||
background: #e3f2fd;
|
||
border: 1px solid #bbdefb;
|
||
border-radius: 8px;
|
||
padding: 15px;
|
||
margin: 0 0 20px 0;
|
||
text-align: center;
|
||
}
|
||
|
||
.matching-feedback {
|
||
background: white;
|
||
border-radius: 12px;
|
||
padding: 20px;
|
||
margin: 20px 0;
|
||
text-align: center;
|
||
}
|
||
|
||
@media (max-width: 768px) {
|
||
.matching-grid {
|
||
gap: 6px;
|
||
padding: 8px;
|
||
}
|
||
|
||
.matching-grid.grid-3x4 {
|
||
width: min(100%, calc(100vh - 260px) * 4/3);
|
||
height: min(calc(100vw - 30px) * 3/4, calc(100vh - 260px));
|
||
}
|
||
|
||
.matching-grid.grid-4x4 {
|
||
width: min(100%, calc(100vh - 260px));
|
||
height: min(calc(100vw - 30px), calc(100vh - 260px));
|
||
}
|
||
|
||
.matching-grid.grid-4x6 {
|
||
width: min(100%, calc(100vh - 260px) * 6/4);
|
||
height: min(calc(100vw - 30px) * 4/6, calc(100vh - 260px));
|
||
}
|
||
|
||
.matching-grid.grid-5x6 {
|
||
width: min(100%, calc(100vh - 260px) * 6/5);
|
||
height: min(calc(100vw - 30px) * 5/6, calc(100vh - 260px));
|
||
}
|
||
|
||
.match-card-number {
|
||
font-size: 16px;
|
||
}
|
||
|
||
.match-card-back .card-type-icon {
|
||
font-size: 28px;
|
||
}
|
||
|
||
.matching-header {
|
||
padding: 10px 15px;
|
||
margin: 0 -20px 10px -20px;
|
||
}
|
||
|
||
.matching-instructions {
|
||
padding: 12px;
|
||
margin: 0 0 15px 0;
|
||
font-size: 14px;
|
||
}
|
||
|
||
.start-game-btn {
|
||
min-height: 60px;
|
||
padding: 12px 16px;
|
||
}
|
||
|
||
.start-game-btn .btn-main {
|
||
font-size: 14px;
|
||
}
|
||
|
||
.start-game-btn .btn-sub {
|
||
font-size: 12px;
|
||
}
|
||
}
|
||
|
||
@media (max-width: 480px) {
|
||
.matching-grid {
|
||
gap: 4px;
|
||
padding: 5px;
|
||
}
|
||
|
||
.matching-grid.grid-3x4 {
|
||
width: min(100%, calc(100vh - 240px) * 4/3);
|
||
height: min(calc(100vw - 20px) * 3/4, calc(100vh - 240px));
|
||
}
|
||
|
||
.matching-grid.grid-4x4 {
|
||
width: min(100%, calc(100vh - 240px));
|
||
height: min(calc(100vw - 20px), calc(100vh - 240px));
|
||
}
|
||
|
||
.matching-grid.grid-4x6 {
|
||
width: min(100%, calc(100vh - 240px) * 6/4);
|
||
height: min(calc(100vw - 20px) * 4/6, calc(100vh - 240px));
|
||
}
|
||
|
||
.matching-grid.grid-5x6 {
|
||
width: min(100%, calc(100vh - 240px) * 6/5);
|
||
height: min(calc(100vw - 20px) * 5/6, calc(100vh - 240px));
|
||
}
|
||
|
||
.match-card-number {
|
||
font-size: 14px;
|
||
}
|
||
|
||
.match-card-back .card-type-icon {
|
||
font-size: 24px;
|
||
}
|
||
|
||
.start-game-btn {
|
||
min-height: 50px;
|
||
padding: 10px 12px;
|
||
}
|
||
|
||
.start-game-btn .btn-main {
|
||
font-size: 13px;
|
||
}
|
||
|
||
.start-game-btn .btn-sub {
|
||
font-size: 11px;
|
||
}
|
||
|
||
.match-card.matched::after {
|
||
width: 10px;
|
||
height: 10px;
|
||
bottom: 2px;
|
||
left: 2px;
|
||
}
|
||
}
|
||
|
||
/* Modal Styling */
|
||
.modal {
|
||
display: none;
|
||
position: fixed;
|
||
z-index: 1000;
|
||
left: 0;
|
||
top: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
background: rgba(0, 0, 0, 0.5);
|
||
animation: fadeIn 0.3s ease-out;
|
||
}
|
||
|
||
.modal.show {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
.modal-content {
|
||
background: white;
|
||
border-radius: 12px;
|
||
width: 90%;
|
||
max-width: 900px;
|
||
max-height: 90vh;
|
||
display: flex;
|
||
flex-direction: column;
|
||
animation: slideIn 0.3s ease-out;
|
||
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.3);
|
||
}
|
||
|
||
.modal-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
padding: 20px 30px;
|
||
border-bottom: 1px solid #eee;
|
||
background: linear-gradient(135deg, #f8f9fa, #e9ecef);
|
||
border-radius: 12px 12px 0 0;
|
||
}
|
||
|
||
.modal-header h2 {
|
||
margin: 0;
|
||
color: #333;
|
||
}
|
||
|
||
.modal-controls {
|
||
display: flex;
|
||
gap: 10px;
|
||
}
|
||
|
||
.fullscreen-btn, .close-btn {
|
||
background: none;
|
||
border: none;
|
||
font-size: 24px;
|
||
cursor: pointer;
|
||
padding: 5px 10px;
|
||
border-radius: 6px;
|
||
transition: background 0.2s ease;
|
||
}
|
||
|
||
.fullscreen-btn:hover, .close-btn:hover {
|
||
background: rgba(0, 0, 0, 0.1);
|
||
}
|
||
|
||
.close-btn {
|
||
color: #666;
|
||
}
|
||
|
||
.close-btn:hover {
|
||
color: #333;
|
||
}
|
||
|
||
.modal-body {
|
||
padding: 30px;
|
||
flex: 1;
|
||
overflow-y: auto;
|
||
}
|
||
|
||
/* Fullscreen modal styling */
|
||
.modal.fullscreen {
|
||
background: rgba(0, 0, 0, 0.95);
|
||
}
|
||
|
||
.modal.fullscreen .modal-content {
|
||
width: 100%;
|
||
height: 100%;
|
||
max-width: none;
|
||
max-height: none;
|
||
border-radius: 0;
|
||
}
|
||
|
||
.modal.fullscreen .modal-header {
|
||
border-radius: 0;
|
||
}
|
||
|
||
/* Animations */
|
||
@keyframes fadeIn {
|
||
from { opacity: 0; }
|
||
to { opacity: 1; }
|
||
}
|
||
|
||
@keyframes slideIn {
|
||
from { transform: scale(0.9) translateY(-20px); opacity: 0; }
|
||
to { transform: scale(1) translateY(0); opacity: 1; }
|
||
}
|
||
|
||
/* Speed Complement Race Enhanced UI Styles */
|
||
.complement-intro {
|
||
text-align: center;
|
||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||
color: white;
|
||
padding: 30px;
|
||
border-radius: 15px;
|
||
margin-bottom: 30px;
|
||
position: relative;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.complement-intro::before {
|
||
content: '';
|
||
position: absolute;
|
||
top: -50%;
|
||
left: -50%;
|
||
width: 200%;
|
||
height: 200%;
|
||
background: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><circle cx="20" cy="20" r="2" fill="white" opacity="0.1"/><circle cx="80" cy="30" r="1.5" fill="white" opacity="0.1"/><circle cx="60" cy="70" r="1" fill="white" opacity="0.15"/></svg>');
|
||
animation: float 15s ease-in-out infinite;
|
||
}
|
||
|
||
.intro-icon {
|
||
font-size: 4rem;
|
||
margin-bottom: 15px;
|
||
display: block;
|
||
filter: drop-shadow(0 4px 8px rgba(0,0,0,0.3));
|
||
}
|
||
|
||
.complement-intro h3 {
|
||
margin: 0 0 15px 0;
|
||
font-size: 2rem;
|
||
font-weight: 700;
|
||
text-shadow: 0 2px 4px rgba(0,0,0,0.3);
|
||
position: relative;
|
||
z-index: 1;
|
||
}
|
||
|
||
.intro-description {
|
||
font-size: 1.1rem;
|
||
margin-bottom: 25px;
|
||
opacity: 0.95;
|
||
position: relative;
|
||
z-index: 1;
|
||
line-height: 1.4;
|
||
}
|
||
|
||
.example-demo {
|
||
background: rgba(255, 255, 255, 0.15);
|
||
border-radius: 12px;
|
||
padding: 20px;
|
||
backdrop-filter: blur(10px);
|
||
position: relative;
|
||
z-index: 1;
|
||
}
|
||
|
||
.demo-equation {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
gap: 15px;
|
||
margin-bottom: 10px;
|
||
font-size: 2rem;
|
||
font-weight: bold;
|
||
}
|
||
|
||
.demo-number {
|
||
background: #ff6b6b;
|
||
color: white;
|
||
padding: 10px 15px;
|
||
border-radius: 10px;
|
||
box-shadow: 0 4px 8px rgba(0,0,0,0.2);
|
||
}
|
||
|
||
.demo-answer {
|
||
background: #feca57;
|
||
color: #333;
|
||
padding: 10px 15px;
|
||
border-radius: 10px;
|
||
box-shadow: 0 4px 8px rgba(0,0,0,0.2);
|
||
animation: pulse 2s infinite;
|
||
}
|
||
|
||
.demo-target {
|
||
background: #48dbfb;
|
||
color: white;
|
||
padding: 10px 15px;
|
||
border-radius: 10px;
|
||
box-shadow: 0 4px 8px rgba(0,0,0,0.2);
|
||
}
|
||
|
||
.demo-plus, .demo-equals {
|
||
color: white;
|
||
font-weight: bold;
|
||
text-shadow: 0 2px 4px rgba(0,0,0,0.3);
|
||
}
|
||
|
||
.demo-instruction {
|
||
font-size: 1.1rem;
|
||
color: #fff;
|
||
opacity: 0.9;
|
||
}
|
||
|
||
/* Enhanced Game Display */
|
||
.complement-display {
|
||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||
border-radius: 20px;
|
||
padding: 40px;
|
||
margin-bottom: 30px;
|
||
color: white;
|
||
position: relative;
|
||
overflow: hidden;
|
||
box-shadow: 0 20px 40px rgba(102, 126, 234, 0.3);
|
||
}
|
||
|
||
.complement-display::before {
|
||
content: '';
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
bottom: 0;
|
||
background: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><circle cx="25" cy="25" r="1" fill="white" opacity="0.1"/><circle cx="75" cy="75" r="1" fill="white" opacity="0.1"/></svg>');
|
||
pointer-events: none;
|
||
}
|
||
|
||
.challenge-prompt {
|
||
text-align: center;
|
||
position: relative;
|
||
z-index: 1;
|
||
margin-bottom: 30px;
|
||
}
|
||
|
||
|
||
/* Coal Spilling Animation */
|
||
@keyframes coalSpill {
|
||
0% {
|
||
transform: translateY(0px) rotate(0deg);
|
||
opacity: 1;
|
||
}
|
||
50% {
|
||
transform: translateY(40px) translateX(20px) rotate(180deg);
|
||
opacity: 0.8;
|
||
}
|
||
100% {
|
||
transform: translateY(80px) translateX(40px) rotate(360deg);
|
||
opacity: 0;
|
||
}
|
||
}
|
||
|
||
.coal-chunk {
|
||
color: #2c2c2c;
|
||
text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.5);
|
||
}
|
||
|
||
/* Pressure Alarm Animation */
|
||
@keyframes pressureAlarm {
|
||
0%, 100% {
|
||
filter: brightness(1) saturate(1);
|
||
transform: scale(1);
|
||
}
|
||
25% {
|
||
filter: brightness(1.5) saturate(2) hue-rotate(0deg);
|
||
transform: scale(1.05);
|
||
}
|
||
50% {
|
||
filter: brightness(2) saturate(3) hue-rotate(15deg);
|
||
transform: scale(1.1);
|
||
}
|
||
75% {
|
||
filter: brightness(1.5) saturate(2) hue-rotate(-15deg);
|
||
transform: scale(1.05);
|
||
}
|
||
}
|
||
|
||
/* Route Completion Celebration */
|
||
.route-completion-celebration {
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
background: rgba(0, 0, 0, 0.8);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
z-index: 1000;
|
||
animation: celebrationFadeIn 0.5s ease-out;
|
||
}
|
||
|
||
.celebration-content {
|
||
background: linear-gradient(135deg, #ffd700, #ffed4e);
|
||
color: #333;
|
||
padding: 40px;
|
||
border-radius: 20px;
|
||
text-align: center;
|
||
box-shadow: 0 20px 40px rgba(255, 215, 0, 0.3);
|
||
animation: celebrationBounce 0.8s ease-out;
|
||
max-width: 400px;
|
||
}
|
||
|
||
.celebration-content h2 {
|
||
margin: 0 0 15px 0;
|
||
font-size: 1.8rem;
|
||
font-weight: 700;
|
||
}
|
||
|
||
.celebration-content p {
|
||
margin: 0 0 20px 0;
|
||
font-size: 1.1rem;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.progress-stats {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 8px;
|
||
font-size: 0.95rem;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.progress-stats div {
|
||
background: rgba(255, 255, 255, 0.3);
|
||
padding: 8px 12px;
|
||
border-radius: 8px;
|
||
}
|
||
|
||
@keyframes celebrationFadeIn {
|
||
from {
|
||
opacity: 0;
|
||
}
|
||
to {
|
||
opacity: 1;
|
||
}
|
||
}
|
||
|
||
@keyframes celebrationBounce {
|
||
0% {
|
||
transform: scale(0.3) rotate(-10deg);
|
||
opacity: 0;
|
||
}
|
||
50% {
|
||
transform: scale(1.05) rotate(2deg);
|
||
}
|
||
100% {
|
||
transform: scale(1) rotate(0deg);
|
||
opacity: 1;
|
||
}
|
||
}
|
||
|
||
|
||
.equation-visual {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
gap: 25px;
|
||
flex-wrap: wrap;
|
||
}
|
||
|
||
.challenge-number-container,
|
||
.answer-container,
|
||
.target-container {
|
||
text-align: center;
|
||
position: relative;
|
||
}
|
||
|
||
.challenge-number {
|
||
font-size: 4rem;
|
||
font-weight: 700;
|
||
background: linear-gradient(135deg, #ff6b6b, #feca57);
|
||
color: white;
|
||
padding: 20px 25px;
|
||
border-radius: 20px;
|
||
box-shadow: 0 10px 25px rgba(255, 107, 107, 0.4);
|
||
text-shadow: 0 2px 4px rgba(0,0,0,0.3);
|
||
animation: bounceIn 1s ease-out;
|
||
}
|
||
|
||
.answer-display {
|
||
font-size: 4rem;
|
||
font-weight: 700;
|
||
background: linear-gradient(135deg, #feca57, #ff9ff3);
|
||
color: white;
|
||
padding: 20px 25px;
|
||
border-radius: 20px;
|
||
box-shadow: 0 10px 25px rgba(254, 202, 87, 0.4);
|
||
text-shadow: 0 2px 4px rgba(0,0,0,0.3);
|
||
text-align: center;
|
||
min-width: 100px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
animation: pulse 2s infinite;
|
||
transition: all 0.3s ease;
|
||
user-select: none;
|
||
}
|
||
|
||
.answer-display.typing {
|
||
animation: none;
|
||
transform: scale(1.1);
|
||
box-shadow: 0 0 0 4px rgba(255, 255, 255, 0.3), 0 15px 35px rgba(254, 202, 87, 0.6);
|
||
background: linear-gradient(135deg, #ff9ff3, #feca57);
|
||
}
|
||
|
||
.answer-display.empty {
|
||
color: rgba(255, 255, 255, 0.7);
|
||
}
|
||
|
||
.target-number {
|
||
font-size: 4rem;
|
||
font-weight: 700;
|
||
background: linear-gradient(135deg, #48dbfb, #0abde3);
|
||
color: white;
|
||
padding: 20px 25px;
|
||
border-radius: 20px;
|
||
box-shadow: 0 10px 25px rgba(72, 219, 251, 0.4);
|
||
text-shadow: 0 2px 4px rgba(0,0,0,0.3);
|
||
}
|
||
|
||
.equation-symbol {
|
||
font-size: 3rem;
|
||
font-weight: bold;
|
||
color: white;
|
||
text-shadow: 0 2px 4px rgba(0,0,0,0.3);
|
||
}
|
||
|
||
.number-label,
|
||
.answer-label,
|
||
.target-label {
|
||
font-size: 0.9rem;
|
||
margin-top: 8px;
|
||
opacity: 0.8;
|
||
font-weight: 500;
|
||
text-transform: uppercase;
|
||
letter-spacing: 1px;
|
||
}
|
||
|
||
.answer-arrow {
|
||
font-size: 1.5rem;
|
||
margin-top: 5px;
|
||
animation: bounce 1s infinite;
|
||
}
|
||
|
||
/* Enhanced Timer */
|
||
.timer-section {
|
||
text-align: center;
|
||
position: relative;
|
||
z-index: 1;
|
||
}
|
||
|
||
.timer-label {
|
||
font-size: 1.1rem;
|
||
margin-bottom: 10px;
|
||
opacity: 0.9;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.timer-bar {
|
||
background: rgba(255, 255, 255, 0.2);
|
||
height: 12px;
|
||
border-radius: 25px;
|
||
overflow: hidden;
|
||
margin-bottom: 10px;
|
||
box-shadow: inset 0 2px 4px rgba(0,0,0,0.2);
|
||
}
|
||
|
||
.timer-fill {
|
||
height: 100%;
|
||
background: linear-gradient(90deg, #ff6b6b, #feca57, #ff6b6b);
|
||
border-radius: 25px;
|
||
transition: width 0.1s linear;
|
||
box-shadow: 0 0 10px rgba(255, 107, 107, 0.6);
|
||
animation: shimmer 2s infinite;
|
||
}
|
||
|
||
.timer-text {
|
||
font-size: 1.3rem;
|
||
font-weight: bold;
|
||
text-shadow: 0 2px 4px rgba(0,0,0,0.3);
|
||
}
|
||
|
||
/* Race Track Styles */
|
||
.race-track-section {
|
||
margin-top: 25px;
|
||
padding: 50px 150px 20px 150px;
|
||
background: rgba(255, 255, 255, 0.1);
|
||
border-radius: 15px;
|
||
border: 2px solid rgba(255, 255, 255, 0.2);
|
||
}
|
||
|
||
@media (max-width: 768px) {
|
||
.race-track-section {
|
||
padding: 30px 80px 20px 80px;
|
||
}
|
||
}
|
||
|
||
@media (max-width: 480px) {
|
||
.race-track-section {
|
||
padding: 30px 40px 20px 40px;
|
||
}
|
||
}
|
||
|
||
.race-label {
|
||
text-align: center;
|
||
font-size: 1.2rem;
|
||
font-weight: bold;
|
||
margin-bottom: 20px;
|
||
color: white;
|
||
text-shadow: 0 2px 4px rgba(0,0,0,0.3);
|
||
}
|
||
|
||
.race-track {
|
||
position: relative;
|
||
height: 150px;
|
||
background: linear-gradient(to right, #4CAF50 0%, #8BC34A 25%, #CDDC39 50%, #FFC107 75%, #FF5722 100%);
|
||
border-radius: 60px;
|
||
margin: 0;
|
||
overflow: visible;
|
||
box-shadow: inset 0 4px 8px rgba(0,0,0,0.2), 0 4px 12px rgba(0,0,0,0.3);
|
||
}
|
||
|
||
.track-background {
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
bottom: 0;
|
||
background: repeating-linear-gradient(
|
||
90deg,
|
||
transparent 0px,
|
||
transparent 18px,
|
||
rgba(255,255,255,0.1) 18px,
|
||
rgba(255,255,255,0.1) 20px
|
||
);
|
||
border-radius: 60px;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.track-line {
|
||
position: absolute;
|
||
top: 0;
|
||
bottom: 0;
|
||
width: 3px;
|
||
background: rgba(255, 255, 255, 0.6);
|
||
box-shadow: 0 0 6px rgba(255,255,255,0.8);
|
||
}
|
||
|
||
.start-line { left: 5%; }
|
||
.quarter-line { left: 25%; opacity: 0.5; }
|
||
.half-line { left: 50%; }
|
||
.three-quarter-line { left: 75%; opacity: 0.5; }
|
||
.finish-line { left: 95%; }
|
||
|
||
.racer {
|
||
position: absolute;
|
||
width: 50px;
|
||
height: 50px;
|
||
background: white;
|
||
border-radius: 50%;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
transition: left 0.6s ease-out, transform 0.3s ease;
|
||
cursor: pointer;
|
||
box-shadow: 0 4px 12px rgba(0,0,0,0.3);
|
||
left: 2%;
|
||
z-index: 10;
|
||
}
|
||
|
||
.racer:hover {
|
||
transform: scale(1.1);
|
||
}
|
||
|
||
.player-racer {
|
||
top: 10px;
|
||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||
border: 3px solid #FFD700;
|
||
z-index: 10;
|
||
}
|
||
|
||
.ai-racer {
|
||
background: linear-gradient(135deg, #ff6b6b 0%, #feca57 100%);
|
||
border: 2px solid #ff4757;
|
||
}
|
||
|
||
#ai-racer-1 {
|
||
top: 35px;
|
||
z-index: 8;
|
||
}
|
||
|
||
#ai-racer-2 {
|
||
top: 60px;
|
||
z-index: 9;
|
||
}
|
||
|
||
.racer-character {
|
||
font-size: 1.5rem;
|
||
line-height: 1;
|
||
animation: bounce 1s infinite alternate;
|
||
}
|
||
|
||
.racer-label {
|
||
position: absolute;
|
||
bottom: -25px;
|
||
left: 50%;
|
||
transform: translateX(-50%);
|
||
font-size: 0.75rem;
|
||
font-weight: bold;
|
||
color: white;
|
||
background: rgba(0,0,0,0.7);
|
||
padding: 2px 6px;
|
||
border-radius: 8px;
|
||
white-space: nowrap;
|
||
text-shadow: none;
|
||
}
|
||
|
||
.racer-progress {
|
||
position: absolute;
|
||
top: -20px;
|
||
left: 50%;
|
||
transform: translateX(-50%);
|
||
font-size: 0.7rem;
|
||
font-weight: bold;
|
||
color: #FFD700;
|
||
background: rgba(0,0,0,0.8);
|
||
padding: 1px 4px;
|
||
border-radius: 6px;
|
||
text-shadow: none;
|
||
}
|
||
|
||
.finish-zone {
|
||
position: absolute;
|
||
right: 0;
|
||
top: 0;
|
||
bottom: 0;
|
||
width: 60px;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
background: linear-gradient(135deg, #FFD700 0%, #FFA500 100%);
|
||
border-top-right-radius: 60px;
|
||
border-bottom-right-radius: 60px;
|
||
border-top-left-radius: 0;
|
||
border-bottom-left-radius: 0;
|
||
z-index: 2;
|
||
}
|
||
|
||
.finish-flag {
|
||
font-size: 1.5rem;
|
||
animation: wave 1s infinite ease-in-out;
|
||
}
|
||
|
||
.finish-text {
|
||
font-size: 0.6rem;
|
||
font-weight: bold;
|
||
color: #333;
|
||
text-shadow: 0 1px 2px rgba(255,255,255,0.8);
|
||
transform: rotate(-90deg);
|
||
margin-top: 5px;
|
||
}
|
||
|
||
.race-stats {
|
||
display: flex;
|
||
justify-content: center;
|
||
gap: 30px;
|
||
margin-top: 15px;
|
||
color: white;
|
||
}
|
||
|
||
.race-stat {
|
||
text-align: center;
|
||
background: rgba(255, 255, 255, 0.1);
|
||
padding: 8px 15px;
|
||
border-radius: 15px;
|
||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||
}
|
||
|
||
.race-stat .stat-label {
|
||
font-size: 0.85rem;
|
||
opacity: 0.9;
|
||
margin-right: 5px;
|
||
}
|
||
|
||
/* Race Animations */
|
||
@keyframes bounce {
|
||
0% { transform: translateY(0); }
|
||
100% { transform: translateY(-3px); }
|
||
}
|
||
|
||
@keyframes wave {
|
||
0%, 100% { transform: rotate(-10deg); }
|
||
50% { transform: rotate(10deg); }
|
||
}
|
||
|
||
/* Circular Track for Survival Mode */
|
||
.race-track-section.circular-track {
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
padding: 40px;
|
||
/* Prevent container from affecting track shape */
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
/* Infinite Mode Track for Sprint Mode */
|
||
.race-track-section.infinite-mode {
|
||
background: linear-gradient(90deg, #667eea 0%, #764ba2 50%, #667eea 100%);
|
||
position: relative;
|
||
}
|
||
|
||
.race-track-section.infinite-mode::before {
|
||
content: '⚡ TIME-BASED CHALLENGE ⚡';
|
||
position: absolute;
|
||
top: 10px;
|
||
left: 50%;
|
||
transform: translateX(-50%);
|
||
background: rgba(255, 215, 0, 0.9);
|
||
color: #333;
|
||
padding: 8px 16px;
|
||
border-radius: 20px;
|
||
font-size: 0.85rem;
|
||
font-weight: 600;
|
||
letter-spacing: 1px;
|
||
z-index: 10;
|
||
}
|
||
|
||
.race-track-section.infinite-mode .race-track {
|
||
background: linear-gradient(90deg,
|
||
transparent 0%,
|
||
rgba(255,255,255,0.1) 10%,
|
||
rgba(255,255,255,0.2) 50%,
|
||
rgba(255,255,255,0.1) 90%,
|
||
transparent 100%);
|
||
border: 2px dashed rgba(255, 215, 0, 0.6);
|
||
}
|
||
|
||
/* Steam Train Journey Visualization for Sprint Mode */
|
||
.race-track-section.steam-journey {
|
||
/* Dynamic sky gradient based on time of day with smooth transitions */
|
||
background: var(--sky-gradient, var(--dawn-gradient));
|
||
transition: --sky-gradient 3s ease-in-out, background 2s ease-out;
|
||
padding: 20px;
|
||
height: 400px;
|
||
max-height: 400px;
|
||
position: relative;
|
||
overflow: hidden;
|
||
}
|
||
|
||
|
||
/* Underground tunnel visualization */
|
||
.tunnel-container {
|
||
display: flex;
|
||
justify-content: space-around;
|
||
align-items: flex-start;
|
||
padding: 60px 40px 20px;
|
||
height: 100%;
|
||
position: relative;
|
||
max-height: 240px; /* Constrain to fit in container */
|
||
}
|
||
|
||
.tunnel-shaft {
|
||
width: 80px;
|
||
background: #4a4a4a;
|
||
border: 3px solid #333;
|
||
border-radius: 8px;
|
||
position: relative;
|
||
min-height: 50px;
|
||
transition: height 0.3s ease-out;
|
||
box-shadow: inset 0 0 20px rgba(0,0,0,0.5);
|
||
}
|
||
|
||
.tunnel-shaft::before {
|
||
content: '';
|
||
position: absolute;
|
||
top: -20px;
|
||
left: 50%;
|
||
transform: translateX(-50%);
|
||
width: 60px;
|
||
height: 20px;
|
||
background: #90EE90;
|
||
border-radius: 50%;
|
||
border: 2px solid #6B8E23;
|
||
}
|
||
|
||
.fox-digger {
|
||
position: absolute;
|
||
bottom: 10px; /* Position inside tunnel, not at the bottom */
|
||
left: 50%;
|
||
transform: translateX(-50%);
|
||
font-size: 2rem;
|
||
z-index: 5;
|
||
transition: all 0.3s ease;
|
||
}
|
||
|
||
.fox-digger.digging {
|
||
animation: foxDigging 0.6s ease-in-out;
|
||
}
|
||
|
||
.fox-digger.idle {
|
||
animation: foxBreathe 2s ease-in-out infinite;
|
||
}
|
||
|
||
/* Active digging animation */
|
||
@keyframes foxDigging {
|
||
0% {
|
||
transform: translateX(-50%) translateY(0) rotate(0deg);
|
||
}
|
||
20% {
|
||
transform: translateX(-50%) translateY(-8px) rotate(-10deg) scaleY(0.9);
|
||
}
|
||
40% {
|
||
transform: translateX(-50%) translateY(8px) rotate(10deg) scaleY(1.1);
|
||
}
|
||
60% {
|
||
transform: translateX(-50%) translateY(-5px) rotate(-5deg) scaleY(0.95);
|
||
}
|
||
80% {
|
||
transform: translateX(-50%) translateY(3px) rotate(5deg) scaleY(1.05);
|
||
}
|
||
100% {
|
||
transform: translateX(-50%) translateY(0) rotate(0deg);
|
||
}
|
||
}
|
||
|
||
/* Breathing animation when idle */
|
||
@keyframes foxBreathe {
|
||
0%, 100% {
|
||
transform: translateX(-50%) scale(1);
|
||
}
|
||
50% {
|
||
transform: translateX(-50%) scale(1.02);
|
||
}
|
||
}
|
||
|
||
/* Deeper foxes appear smaller (perspective effect) */
|
||
.tunnel-shaft.depth-deep .fox-digger {
|
||
font-size: 1.8rem;
|
||
bottom: 15px;
|
||
}
|
||
|
||
.tunnel-shaft.depth-very-deep .fox-digger {
|
||
font-size: 1.6rem;
|
||
bottom: 20px;
|
||
animation: foxDigDeep 0.8s ease-in-out;
|
||
}
|
||
|
||
@keyframes foxDigDeep {
|
||
0% {
|
||
transform: translateX(-50%) translateY(0) rotate(0deg) scale(1);
|
||
}
|
||
25% {
|
||
transform: translateX(-50%) translateY(-10px) rotate(-15deg) scale(0.9) scaleY(0.8);
|
||
}
|
||
50% {
|
||
transform: translateX(-50%) translateY(10px) rotate(15deg) scale(1.1) scaleY(1.2);
|
||
}
|
||
75% {
|
||
transform: translateX(-50%) translateY(-5px) rotate(-8deg) scale(0.95);
|
||
}
|
||
100% {
|
||
transform: translateX(-50%) translateY(0) rotate(0deg) scale(1);
|
||
}
|
||
}
|
||
|
||
.tunnel-depth {
|
||
position: absolute;
|
||
bottom: -25px;
|
||
left: 50%;
|
||
transform: translateX(-50%);
|
||
background: rgba(255, 255, 255, 0.9);
|
||
color: #333;
|
||
padding: 4px 8px;
|
||
border-radius: 12px;
|
||
font-size: 0.8rem;
|
||
font-weight: 600;
|
||
min-width: 40px;
|
||
text-align: center;
|
||
}
|
||
|
||
.tunnel-player .tunnel-depth {
|
||
background: rgba(34, 197, 94, 0.9);
|
||
color: white;
|
||
}
|
||
|
||
.tunnel-ai .tunnel-depth {
|
||
background: rgba(239, 68, 68, 0.9);
|
||
color: white;
|
||
}
|
||
|
||
/* Dirt particles effect when digging */
|
||
@keyframes dirtFly {
|
||
0% {
|
||
opacity: 1;
|
||
transform: translate(0, 0) scale(1);
|
||
}
|
||
100% {
|
||
opacity: 0;
|
||
transform: translate(var(--fly-x), var(--fly-y)) scale(0.3);
|
||
}
|
||
}
|
||
|
||
.dirt-particle {
|
||
position: absolute;
|
||
width: 4px;
|
||
height: 4px;
|
||
background: #8B4513;
|
||
border-radius: 50%;
|
||
animation: dirtFly 0.6s ease-out forwards;
|
||
pointer-events: none;
|
||
}
|
||
|
||
/* Treasure animations */
|
||
@keyframes treasureFloat {
|
||
0% {
|
||
opacity: 0;
|
||
transform: translate(-50%, 0) scale(0);
|
||
}
|
||
20% {
|
||
opacity: 1;
|
||
transform: translate(-50%, -20px) scale(1.2);
|
||
}
|
||
100% {
|
||
opacity: 0;
|
||
transform: translate(-50%, -80px) scale(0.8);
|
||
}
|
||
}
|
||
|
||
@keyframes treasureSlideIn {
|
||
0% {
|
||
opacity: 0;
|
||
transform: translateX(100px);
|
||
}
|
||
20% {
|
||
opacity: 1;
|
||
transform: translateX(0);
|
||
}
|
||
80% {
|
||
opacity: 1;
|
||
transform: translateX(0);
|
||
}
|
||
100% {
|
||
opacity: 0;
|
||
transform: translateX(-100px);
|
||
}
|
||
}
|
||
|
||
/* Speech bubbles for tunnel mode */
|
||
.tunnel-speech {
|
||
position: absolute;
|
||
top: -40px;
|
||
left: 50%;
|
||
transform: translateX(-50%);
|
||
}
|
||
|
||
/* Screen shake animation for deep digging */
|
||
@keyframes screenShake {
|
||
0%, 100% { transform: translateX(0); }
|
||
25% { transform: translateX(-2px); }
|
||
75% { transform: translateX(2px); }
|
||
}
|
||
|
||
/* Day/Night Cycle CSS Variables */
|
||
:root {
|
||
/* Dawn (0-17%) */
|
||
--dawn-gradient: linear-gradient(180deg,
|
||
#4a5568 0%, #667eea 20%, #f093fb 40%, #f5deb3 60%, #90EE90 80%, #654321 100%);
|
||
/* Morning (17-33%) */
|
||
--morning-gradient: linear-gradient(180deg,
|
||
#87ceeb 0%, #f0e68c 20%, #90EE90 50%, #8FBC8F 80%, #654321 100%);
|
||
/* Midday (33-67%) */
|
||
--midday-gradient: linear-gradient(180deg,
|
||
#87ceeb 0%, #87ceeb 30%, #90EE90 60%, #228B22 80%, #654321 100%);
|
||
/* Afternoon (67-83%) */
|
||
--afternoon-gradient: linear-gradient(180deg,
|
||
#ff7f50 0%, #ffd700 20%, #90EE90 50%, #8B4513 80%, #654321 100%);
|
||
/* Dusk (83-92%) */
|
||
--dusk-gradient: linear-gradient(180deg,
|
||
#4b0082 0%, #ff6347 30%, #ffa500 50%, #8B4513 70%, #2f4f4f 100%);
|
||
/* Night (92-100%) */
|
||
--night-gradient: linear-gradient(180deg,
|
||
#191970 0%, #2f4f4f 40%, #1a1a1a 70%, #000000 100%);
|
||
}
|
||
|
||
.steam-journey-header {
|
||
position: absolute;
|
||
top: 10px;
|
||
left: 50%;
|
||
transform: translateX(-50%);
|
||
background: rgba(255, 215, 0, 0.95);
|
||
color: #333;
|
||
padding: 8px 16px;
|
||
border-radius: 20px;
|
||
font-size: 0.9rem;
|
||
font-weight: 700;
|
||
letter-spacing: 1px;
|
||
z-index: 10;
|
||
box-shadow: 0 4px 12px rgba(0,0,0,0.3);
|
||
text-align: center;
|
||
}
|
||
|
||
.route-progress {
|
||
font-size: 0.75rem;
|
||
font-weight: 600;
|
||
margin-top: 4px;
|
||
opacity: 0.8;
|
||
letter-spacing: 0.5px;
|
||
}
|
||
|
||
/* Winding Route Map */
|
||
.route-map {
|
||
position: relative;
|
||
width: 100%;
|
||
height: 350px;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.route-path {
|
||
position: absolute;
|
||
width: 800px;
|
||
height: 600px;
|
||
top: -100px;
|
||
left: 50%;
|
||
transform: translateX(-50%);
|
||
}
|
||
|
||
/* SVG path for the winding route */
|
||
.train-route {
|
||
fill: none;
|
||
stroke: #8B4513;
|
||
stroke-width: 8;
|
||
stroke-dasharray: 15, 5;
|
||
stroke-linecap: round;
|
||
}
|
||
|
||
.train-route-bg {
|
||
fill: none;
|
||
stroke: #654321;
|
||
stroke-width: 12;
|
||
stroke-linecap: round;
|
||
}
|
||
|
||
/* Train locomotive on the route */
|
||
.train-locomotive {
|
||
position: absolute;
|
||
width: 60px;
|
||
height: 40px;
|
||
background: linear-gradient(135deg, #2c3e50, #34495e);
|
||
border-radius: 8px;
|
||
border: 2px solid #1a252f;
|
||
box-shadow: 0 3px 10px rgba(0,0,0,0.4);
|
||
transition: transform 0.2s ease-out, left 0.1s linear, top 0.1s linear;
|
||
transform-origin: center center;
|
||
z-index: 5;
|
||
/* Start at the beginning of the route */
|
||
left: 50px;
|
||
top: 300px;
|
||
transform: translate(-50%, -50%);
|
||
}
|
||
|
||
.train-locomotive::before {
|
||
content: '🚂';
|
||
position: absolute;
|
||
top: 50%;
|
||
left: 50%;
|
||
transform: translate(-50%, -50%) scaleX(-1);
|
||
font-size: 1.8rem;
|
||
z-index: 6;
|
||
}
|
||
|
||
.train-math-display {
|
||
position: absolute;
|
||
background: rgba(255, 255, 255, 0.95);
|
||
border: 2px solid #4a90e2;
|
||
border-radius: 12px;
|
||
padding: 8px 12px;
|
||
font-size: 1.2rem;
|
||
font-weight: bold;
|
||
color: #333;
|
||
box-shadow: 0 4px 12px rgba(0,0,0,0.2);
|
||
z-index: 7;
|
||
pointer-events: none;
|
||
white-space: nowrap;
|
||
transform: translate(-50%, -100%);
|
||
font-family: 'Arial', sans-serif;
|
||
}
|
||
|
||
/* Steam Pressure Instrument Panel */
|
||
.instrument-panel {
|
||
position: absolute;
|
||
bottom: 20px;
|
||
left: 20px;
|
||
z-index: 10;
|
||
}
|
||
|
||
.panel-frame {
|
||
background: linear-gradient(135deg, #667eea, #764ba2);
|
||
border: 3px solid #5a67d8;
|
||
border-radius: 20px;
|
||
padding: 18px;
|
||
box-shadow:
|
||
0 10px 30px rgba(102, 126, 234, 0.3),
|
||
inset 0 2px 8px rgba(255, 255, 255, 0.2);
|
||
position: relative;
|
||
}
|
||
|
||
.panel-background {
|
||
position: absolute;
|
||
top: 6px;
|
||
left: 6px;
|
||
right: 6px;
|
||
bottom: 6px;
|
||
background: linear-gradient(45deg, #667eea, #764ba2);
|
||
border-radius: 12px;
|
||
border: 2px solid rgba(255, 255, 255, 0.1);
|
||
}
|
||
|
||
.pressure-gauge-container {
|
||
position: relative;
|
||
z-index: 2;
|
||
text-align: center;
|
||
}
|
||
|
||
.pressure-gauge {
|
||
display: block;
|
||
margin: 0 auto 10px auto;
|
||
filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.3));
|
||
}
|
||
|
||
.gauge-background {
|
||
filter: drop-shadow(0 1px 3px rgba(0, 0, 0, 0.4));
|
||
}
|
||
|
||
.gauge-progress {
|
||
transition: stroke-dashoffset 0.5s ease-out;
|
||
filter: drop-shadow(0 0 8px rgba(231, 76, 60, 0.6));
|
||
}
|
||
|
||
.pressure-needle {
|
||
transition: transform 0.5s ease-out;
|
||
transform-origin: 100px 100px;
|
||
}
|
||
|
||
.gauge-title {
|
||
color: #ffffff;
|
||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||
font-size: 0.8rem;
|
||
font-weight: 700;
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.5px;
|
||
margin-bottom: 5px;
|
||
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
|
||
}
|
||
|
||
.gauge-value {
|
||
color: #ffd700;
|
||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||
font-size: 0.9rem;
|
||
font-weight: 800;
|
||
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
|
||
}
|
||
|
||
/* Momentum gauge */
|
||
.momentum-display {
|
||
position: absolute;
|
||
bottom: 20px;
|
||
left: 50%;
|
||
transform: translateX(-50%);
|
||
width: 200px;
|
||
z-index: 10;
|
||
}
|
||
|
||
.momentum-gauge {
|
||
width: 100%;
|
||
height: 12px;
|
||
background: rgba(0,0,0,0.3);
|
||
border-radius: 6px;
|
||
border: 1px solid #333;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.momentum-bar {
|
||
height: 100%;
|
||
background: linear-gradient(90deg, #e74c3c 0%, #f39c12 30%, #27ae60 100%);
|
||
transition: width 0.3s ease-out;
|
||
width: 0%;
|
||
}
|
||
|
||
.momentum-label {
|
||
text-align: center;
|
||
color: white;
|
||
font-size: 0.8rem;
|
||
font-weight: 600;
|
||
margin-top: 5px;
|
||
text-shadow: 1px 1px 2px rgba(0,0,0,0.8);
|
||
}
|
||
|
||
/* City stations along the route */
|
||
.train-station {
|
||
position: absolute;
|
||
width: 20px;
|
||
height: 20px;
|
||
background: #8B4513;
|
||
border: 2px solid #654321;
|
||
border-radius: 50%;
|
||
transform: translate(-50%, -50%);
|
||
z-index: 4;
|
||
}
|
||
|
||
.train-station::after {
|
||
content: attr(data-city);
|
||
position: absolute;
|
||
top: 25px;
|
||
left: 50%;
|
||
transform: translateX(-50%);
|
||
background: rgba(0,0,0,0.7);
|
||
color: white;
|
||
padding: 2px 6px;
|
||
border-radius: 8px;
|
||
font-size: 0.7rem;
|
||
white-space: nowrap;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.train-station.active {
|
||
background: #ffd700;
|
||
border-color: #ffb347;
|
||
animation: stationPulse 1s ease-in-out infinite alternate;
|
||
}
|
||
|
||
@keyframes stationPulse {
|
||
0% { transform: translate(-50%, -50%) scale(1); }
|
||
100% { transform: translate(-50%, -50%) scale(1.2); }
|
||
}
|
||
|
||
/* Passenger car display */
|
||
.passenger-car {
|
||
position: absolute;
|
||
bottom: 60px;
|
||
right: 20px;
|
||
background: rgba(255,255,255,0.9);
|
||
border-radius: 10px;
|
||
padding: 10px;
|
||
min-width: 150px;
|
||
box-shadow: 0 4px 12px rgba(0,0,0,0.3);
|
||
}
|
||
|
||
.passenger-car h4 {
|
||
margin: 0 0 8px 0;
|
||
font-size: 0.9rem;
|
||
color: #333;
|
||
}
|
||
|
||
.passenger-list {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: 5px;
|
||
}
|
||
|
||
.passenger {
|
||
background: #667eea;
|
||
color: white;
|
||
padding: 2px 6px;
|
||
border-radius: 12px;
|
||
font-size: 0.7rem;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.passenger.urgent {
|
||
background: #e74c3c;
|
||
animation: passengerUrgent 1s ease-in-out infinite alternate;
|
||
}
|
||
|
||
@keyframes passengerUrgent {
|
||
0% { opacity: 1; }
|
||
100% { opacity: 0.6; }
|
||
}
|
||
|
||
/* Coal shoveling animation - enhanced */
|
||
.coal-shoveler {
|
||
position: absolute;
|
||
bottom: 80px;
|
||
left: 30px;
|
||
font-size: 2rem;
|
||
z-index: 6;
|
||
transition: all 0.3s ease;
|
||
}
|
||
|
||
.coal-shoveler.shoveling {
|
||
animation: shoveling 0.8s ease-in-out;
|
||
}
|
||
|
||
@keyframes shoveling {
|
||
0% { transform: rotate(0deg) scale(1); }
|
||
20% { transform: rotate(-25deg) scale(0.9); }
|
||
40% { transform: rotate(25deg) scale(1.2); filter: brightness(1.3); }
|
||
60% { transform: rotate(-15deg) scale(1.1); }
|
||
80% { transform: rotate(10deg) scale(1.05); }
|
||
100% { transform: rotate(0deg) scale(1); }
|
||
}
|
||
|
||
/* Coal particles effect */
|
||
.coal-particle {
|
||
position: absolute;
|
||
background: #2c2c2c;
|
||
border-radius: 50%;
|
||
pointer-events: none;
|
||
z-index: 4;
|
||
}
|
||
|
||
@keyframes coalFly {
|
||
0% {
|
||
transform: translate(0, 0) scale(1);
|
||
opacity: 1;
|
||
}
|
||
50% {
|
||
transform: translate(-20px, -30px) scale(0.8);
|
||
opacity: 0.8;
|
||
}
|
||
100% {
|
||
transform: translate(-40px, -60px) scale(0.3);
|
||
opacity: 0;
|
||
}
|
||
}
|
||
|
||
/* Steam puffs from locomotive */
|
||
.steam-puff {
|
||
position: absolute;
|
||
background: rgba(255, 255, 255, 0.8);
|
||
border-radius: 50%;
|
||
pointer-events: none;
|
||
z-index: 3;
|
||
}
|
||
|
||
@keyframes steamRise {
|
||
0% {
|
||
transform: translate(0, 0) scale(0.5);
|
||
opacity: 0.9;
|
||
}
|
||
50% {
|
||
transform: translate(10px, -40px) scale(1.2);
|
||
opacity: 0.6;
|
||
}
|
||
100% {
|
||
transform: translate(20px, -80px) scale(2);
|
||
opacity: 0;
|
||
}
|
||
}
|
||
|
||
/* Momentum bar flash effect */
|
||
.momentum-boost {
|
||
animation: momentumBoost 0.8s ease-out;
|
||
}
|
||
|
||
@keyframes momentumBoost {
|
||
0% {
|
||
box-shadow: 0 0 0px rgba(40, 167, 69, 0.6);
|
||
transform: scale(1);
|
||
}
|
||
30% {
|
||
box-shadow: 0 0 20px rgba(40, 167, 69, 0.8);
|
||
transform: scale(1.05);
|
||
}
|
||
100% {
|
||
box-shadow: 0 0 5px rgba(40, 167, 69, 0.3);
|
||
transform: scale(1);
|
||
}
|
||
}
|
||
|
||
/* Steam effects */
|
||
.steam-effect {
|
||
position: absolute;
|
||
top: 10px;
|
||
left: 30px;
|
||
width: 8px;
|
||
height: 8px;
|
||
background: rgba(255,255,255,0.8);
|
||
border-radius: 50%;
|
||
animation: steamRise 2s ease-out infinite;
|
||
z-index: 3;
|
||
}
|
||
|
||
@keyframes steamRise {
|
||
0% {
|
||
opacity: 0.8;
|
||
transform: translateY(0) scale(0.5);
|
||
}
|
||
100% {
|
||
opacity: 0;
|
||
transform: translateY(-60px) scale(2);
|
||
}
|
||
}
|
||
|
||
/* Geographical landmarks */
|
||
.landmark {
|
||
position: absolute;
|
||
font-size: 1.5rem;
|
||
z-index: 2;
|
||
opacity: 0.7;
|
||
}
|
||
|
||
.landmark.mountain { font-size: 2rem; }
|
||
.landmark.tree { font-size: 1.2rem; }
|
||
.landmark.bridge { font-size: 1.8rem; }
|
||
|
||
/* Time of day indicator */
|
||
.time-display {
|
||
position: absolute;
|
||
top: 20px;
|
||
right: 20px;
|
||
background: rgba(0,0,0,0.7);
|
||
color: white;
|
||
padding: 5px 10px;
|
||
border-radius: 15px;
|
||
font-size: 0.8rem;
|
||
font-weight: 600;
|
||
}
|
||
|
||
/* Hide linear track elements when in circular mode */
|
||
.race-track.circular .track-background,
|
||
.race-track.circular .track-line {
|
||
display: none;
|
||
}
|
||
|
||
.race-track.circular {
|
||
width: 400px;
|
||
height: 400px;
|
||
border-radius: 50%;
|
||
position: relative;
|
||
/* Maintain circular aspect ratio */
|
||
flex-shrink: 0;
|
||
aspect-ratio: 1 / 1;
|
||
/* Create donut shape with inner and outer borders */
|
||
background:
|
||
radial-gradient(circle at center, transparent 35%, rgba(34, 197, 94, 0.2) 35%, rgba(34, 197, 94, 0.2) 65%, transparent 65%);
|
||
border: 6px solid rgba(34, 197, 94, 0.6);
|
||
box-shadow:
|
||
inset 0 0 30px rgba(34, 197, 94, 0.3),
|
||
0 0 20px rgba(0,0,0,0.3);
|
||
}
|
||
|
||
/* Responsive circular track for smaller screens */
|
||
@media (max-width: 768px) {
|
||
.race-track.circular {
|
||
width: 300px;
|
||
height: 300px;
|
||
}
|
||
}
|
||
|
||
@media (max-width: 480px) {
|
||
.race-track.circular {
|
||
width: 250px;
|
||
height: 250px;
|
||
}
|
||
}
|
||
|
||
/* Inner donut hole */
|
||
.race-track.circular::before {
|
||
content: '';
|
||
position: absolute;
|
||
top: 50%;
|
||
left: 50%;
|
||
width: 140px;
|
||
height: 140px;
|
||
background: rgba(0,0,0,0.1);
|
||
border: 4px solid rgba(34, 197, 94, 0.4);
|
||
border-radius: 50%;
|
||
transform: translate(-50%, -50%);
|
||
z-index: 1;
|
||
}
|
||
|
||
/* Position racers on circular track */
|
||
.race-track.circular .racer {
|
||
position: absolute;
|
||
width: 40px;
|
||
height: 40px;
|
||
transition: all 0.6s ease-out;
|
||
}
|
||
|
||
/* Counteract rotation for speech bubbles on circular track */
|
||
.race-track.circular .racer .speech-bubble {
|
||
transform: translateY(-50%) rotate(var(--counter-rotation, 0deg));
|
||
}
|
||
|
||
/* Speech Bubbles */
|
||
.speech-bubble {
|
||
position: absolute;
|
||
top: -45px;
|
||
left: 60px;
|
||
transform: translateY(-50%);
|
||
z-index: 200;
|
||
opacity: 0;
|
||
visibility: hidden;
|
||
transition: all 0.4s ease;
|
||
pointer-events: none;
|
||
}
|
||
|
||
.speech-bubble.visible {
|
||
opacity: 1;
|
||
visibility: visible;
|
||
animation: bubblePopIn 0.5s cubic-bezier(0.68, -0.55, 0.265, 1.55);
|
||
}
|
||
|
||
.bubble-content {
|
||
background: white;
|
||
border-radius: 18px;
|
||
padding: 8px 12px;
|
||
min-width: 120px;
|
||
max-width: 200px;
|
||
font-size: 0.8rem;
|
||
font-weight: 600;
|
||
color: #2c3e50;
|
||
text-align: center;
|
||
line-height: 1.2;
|
||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||
border: 2px solid #f0f2f5;
|
||
position: relative;
|
||
}
|
||
|
||
.bubble-content::before {
|
||
content: '';
|
||
position: absolute;
|
||
top: -2px;
|
||
left: -2px;
|
||
right: -2px;
|
||
bottom: -2px;
|
||
border-radius: 20px;
|
||
background: linear-gradient(135deg, #4a90e2, #357abd);
|
||
z-index: -1;
|
||
}
|
||
|
||
.bubble-tail {
|
||
position: absolute;
|
||
top: 50%;
|
||
left: -8px;
|
||
transform: translateY(-50%);
|
||
width: 0;
|
||
height: 0;
|
||
border: 8px solid transparent;
|
||
border-right-color: white;
|
||
z-index: 1;
|
||
}
|
||
|
||
.bubble-tail::before {
|
||
content: '';
|
||
position: absolute;
|
||
top: -9px;
|
||
left: 2px;
|
||
width: 0;
|
||
height: 0;
|
||
border: 9px solid transparent;
|
||
border-right-color: #4a90e2;
|
||
z-index: -1;
|
||
}
|
||
|
||
/* Different bubble styles for different AI personalities */
|
||
#ai-racer-1 .bubble-content {
|
||
background: linear-gradient(135deg, #ff6b6b, #feca57);
|
||
color: white;
|
||
border-color: #ff6b6b;
|
||
}
|
||
|
||
#ai-racer-1 .speech-bubble {
|
||
left: -140px; /* Position behind the racer */
|
||
top: -10px; /* Align with ai-racer-1 which is at top: 35px */
|
||
}
|
||
|
||
#ai-racer-1 .bubble-tail {
|
||
border-right-color: #ff6b6b;
|
||
left: auto;
|
||
right: -8px; /* Point from the right side */
|
||
border-right-color: transparent;
|
||
border-left-color: #ff6b6b;
|
||
}
|
||
|
||
#ai-racer-1 .bubble-tail::before {
|
||
left: auto;
|
||
right: 2px;
|
||
border-right-color: transparent;
|
||
border-left-color: #ff6b6b;
|
||
}
|
||
|
||
#ai-racer-2 .speech-bubble {
|
||
top: 15px; /* Align with ai-racer-2 which is at top: 60px */
|
||
}
|
||
|
||
#ai-racer-2 .bubble-content {
|
||
background: linear-gradient(135deg, #4ecdc4, #44a08d);
|
||
color: white;
|
||
border-color: #4ecdc4;
|
||
}
|
||
|
||
#ai-racer-2 .bubble-tail {
|
||
border-right-color: #4ecdc4;
|
||
}
|
||
|
||
@keyframes bubblePopIn {
|
||
0% {
|
||
opacity: 0;
|
||
transform: translateY(-50%) translateX(10px) scale(0.3);
|
||
}
|
||
50% {
|
||
opacity: 1;
|
||
transform: translateY(-50%) translateX(-5px) scale(1.1);
|
||
}
|
||
100% {
|
||
opacity: 1;
|
||
transform: translateY(-50%) translateX(0) scale(1);
|
||
}
|
||
}
|
||
|
||
/* Player Race Animations */
|
||
.racer.tripped {
|
||
animation: tripAndRecover 1.2s ease-out;
|
||
}
|
||
|
||
.racer.moving-backwards {
|
||
animation: moveBackwards 0.8s ease-out;
|
||
}
|
||
|
||
@keyframes tripAndRecover {
|
||
0% { transform: rotate(0deg) translateY(0px); }
|
||
20% { transform: rotate(-15deg) translateY(8px); }
|
||
40% { transform: rotate(-25deg) translateY(15px); }
|
||
60% { transform: rotate(-20deg) translateY(12px); }
|
||
80% { transform: rotate(-5deg) translateY(3px); }
|
||
100% { transform: rotate(0deg) translateY(0px); }
|
||
}
|
||
|
||
@keyframes moveBackwards {
|
||
0% { transform: translateX(0px) scale(1); }
|
||
30% { transform: translateX(-20px) scale(0.9); }
|
||
60% { transform: translateX(-15px) scale(0.95); }
|
||
100% { transform: translateX(0px) scale(1); }
|
||
}
|
||
|
||
@keyframes raceFinish {
|
||
0% { transform: scale(1) rotate(0deg); }
|
||
50% { transform: scale(1.2) rotate(180deg); }
|
||
100% { transform: scale(1) rotate(360deg); }
|
||
}
|
||
|
||
.racer.winner {
|
||
animation: raceFinish 1s ease-in-out;
|
||
border-color: #FFD700;
|
||
box-shadow: 0 0 20px #FFD700;
|
||
}
|
||
|
||
.racer.celebrating {
|
||
animation: bounce 0.3s infinite alternate;
|
||
}
|
||
|
||
/* Special bounce for circular track that preserves rotation */
|
||
.race-track.circular .racer.celebrating {
|
||
animation: circularBounce 0.3s infinite alternate;
|
||
}
|
||
|
||
@keyframes circularBounce {
|
||
0% {
|
||
transform: rotate(var(--racer-rotation, 0deg)) scale(1) translateY(0px);
|
||
}
|
||
100% {
|
||
transform: rotate(var(--racer-rotation, 0deg)) scale(1.1) translateY(-2px);
|
||
}
|
||
}
|
||
|
||
/* Enhanced Feedback Area */
|
||
.complement-feedback-area {
|
||
text-align: center;
|
||
background: white;
|
||
border-radius: 20px;
|
||
padding: 25px;
|
||
box-shadow: 0 10px 25px rgba(0,0,0,0.1);
|
||
margin-bottom: 30px;
|
||
position: relative;
|
||
}
|
||
|
||
.input-hint {
|
||
font-size: 1.1rem;
|
||
color: #666;
|
||
opacity: 0.8;
|
||
margin-top: 10px;
|
||
}
|
||
|
||
/* Enhanced Game Style Buttons */
|
||
.style-buttons {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 15px;
|
||
margin-top: 15px;
|
||
}
|
||
|
||
.game-style-btn {
|
||
background: white;
|
||
border: 2px solid #e9ecef;
|
||
border-radius: 15px;
|
||
padding: 20px 25px;
|
||
cursor: pointer;
|
||
transition: all 0.3s ease;
|
||
text-align: left;
|
||
width: 100%;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 20px;
|
||
box-shadow: 0 4px 12px rgba(0,0,0,0.08);
|
||
position: relative;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.game-style-btn::before {
|
||
content: '';
|
||
position: absolute;
|
||
top: 0;
|
||
left: -100%;
|
||
width: 100%;
|
||
height: 100%;
|
||
background: linear-gradient(90deg, transparent, rgba(255,255,255,0.4), transparent);
|
||
transition: left 0.6s;
|
||
}
|
||
|
||
.game-style-btn:hover {
|
||
border-color: #667eea;
|
||
transform: translateY(-2px);
|
||
box-shadow: 0 8px 25px rgba(102, 126, 234, 0.15);
|
||
}
|
||
|
||
.game-style-btn:hover::before {
|
||
left: 100%;
|
||
}
|
||
|
||
.game-style-btn.active {
|
||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||
border-color: #667eea;
|
||
color: white;
|
||
box-shadow: 0 8px 25px rgba(102, 126, 234, 0.3);
|
||
}
|
||
|
||
.game-style-btn.active:hover {
|
||
transform: translateY(-3px);
|
||
box-shadow: 0 12px 35px rgba(102, 126, 234, 0.4);
|
||
}
|
||
|
||
.game-style-btn.active::before {
|
||
background: linear-gradient(90deg, transparent, rgba(255,255,255,0.2), transparent);
|
||
}
|
||
|
||
.style-icon {
|
||
font-size: 2.5rem;
|
||
line-height: 1;
|
||
filter: drop-shadow(0 2px 4px rgba(0,0,0,0.1));
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.game-style-btn.active .style-icon {
|
||
filter: drop-shadow(0 2px 4px rgba(0,0,0,0.3));
|
||
}
|
||
|
||
.style-content {
|
||
flex: 1;
|
||
}
|
||
|
||
.style-title {
|
||
font-size: 1.3rem;
|
||
font-weight: 700;
|
||
margin-bottom: 5px;
|
||
color: #333;
|
||
}
|
||
|
||
.game-style-btn.active .style-title {
|
||
color: white;
|
||
}
|
||
|
||
.style-description {
|
||
font-size: 0.95rem;
|
||
color: #666;
|
||
line-height: 1.4;
|
||
opacity: 0.9;
|
||
}
|
||
|
||
.game-style-btn.active .style-description {
|
||
color: rgba(255, 255, 255, 0.9);
|
||
}
|
||
|
||
/* Hover effects for individual styles */
|
||
.game-style-btn[data-style="practice"]:not(.active):hover {
|
||
border-color: #28a745;
|
||
box-shadow: 0 8px 25px rgba(40, 167, 69, 0.15);
|
||
}
|
||
|
||
.game-style-btn[data-style="sprint"]:not(.active):hover {
|
||
border-color: #ffc107;
|
||
box-shadow: 0 8px 25px rgba(255, 193, 7, 0.15);
|
||
}
|
||
|
||
.game-style-btn[data-style="survival"]:not(.active):hover {
|
||
border-color: #dc3545;
|
||
box-shadow: 0 8px 25px rgba(220, 53, 69, 0.15);
|
||
}
|
||
|
||
/* Enhanced Feedback */
|
||
.input-feedback {
|
||
margin-top: 15px;
|
||
font-size: 1.2rem;
|
||
font-weight: 600;
|
||
min-height: 30px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
transition: all 0.3s ease;
|
||
}
|
||
|
||
.input-feedback.correct {
|
||
color: #28a745;
|
||
animation: success 0.6s ease-out;
|
||
}
|
||
|
||
.input-feedback.incorrect {
|
||
color: #dc3545;
|
||
animation: shake 0.6s ease-out;
|
||
}
|
||
|
||
.input-feedback.timeout {
|
||
color: #ffc107;
|
||
animation: fadeIn 0.5s ease-out;
|
||
}
|
||
|
||
/* Adaptive Difficulty Feedback */
|
||
.adaptive-feedback {
|
||
margin-top: 10px;
|
||
font-size: 0.95rem;
|
||
font-weight: 500;
|
||
min-height: 20px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
transition: all 0.4s ease;
|
||
opacity: 0.8;
|
||
}
|
||
|
||
.adaptive-feedback.learning {
|
||
color: #17a2b8;
|
||
background: rgba(23, 162, 184, 0.1);
|
||
border-radius: 15px;
|
||
padding: 8px 16px;
|
||
}
|
||
|
||
.adaptive-feedback.struggling {
|
||
color: #dc3545;
|
||
background: rgba(220, 53, 69, 0.1);
|
||
border-radius: 15px;
|
||
padding: 8px 16px;
|
||
}
|
||
|
||
.adaptive-feedback.mastered {
|
||
color: #28a745;
|
||
background: rgba(40, 167, 69, 0.1);
|
||
border-radius: 15px;
|
||
padding: 8px 16px;
|
||
}
|
||
|
||
.adaptive-feedback.adapted {
|
||
color: #6f42c1;
|
||
background: rgba(111, 66, 193, 0.1);
|
||
border-radius: 15px;
|
||
padding: 8px 16px;
|
||
animation: adaptivePulse 1s ease-out;
|
||
}
|
||
|
||
@keyframes adaptivePulse {
|
||
0% { transform: scale(1); opacity: 0; }
|
||
50% { transform: scale(1.05); opacity: 1; }
|
||
100% { transform: scale(1); opacity: 0.8; }
|
||
}
|
||
|
||
/* Animations */
|
||
@keyframes pulse {
|
||
0%, 100% { transform: scale(1); }
|
||
50% { transform: scale(1.05); }
|
||
}
|
||
|
||
@keyframes glow {
|
||
0% { box-shadow: 0 0 5px rgba(255, 255, 255, 0.5); }
|
||
100% { box-shadow: 0 0 20px rgba(255, 255, 255, 0.8), 0 0 30px rgba(255, 255, 255, 0.6); }
|
||
}
|
||
|
||
@keyframes bounceIn {
|
||
0% { transform: scale(0.3) rotate(-10deg); opacity: 0; }
|
||
50% { transform: scale(1.1) rotate(5deg); }
|
||
100% { transform: scale(1) rotate(0deg); opacity: 1; }
|
||
}
|
||
|
||
@keyframes bounce {
|
||
0%, 100% { transform: translateY(0); }
|
||
50% { transform: translateY(-10px); }
|
||
}
|
||
|
||
@keyframes shimmer {
|
||
0% { background-position: -200px 0; }
|
||
100% { background-position: 200px 0; }
|
||
}
|
||
|
||
/* Lapping Celebration Animations */
|
||
@keyframes lapCelebrationBounce {
|
||
0% {
|
||
transform: translateX(-50%) scale(0);
|
||
opacity: 0;
|
||
rotation: -10deg;
|
||
}
|
||
30% {
|
||
transform: translateX(-50%) scale(1.2);
|
||
opacity: 1;
|
||
rotation: 5deg;
|
||
}
|
||
60% {
|
||
transform: translateX(-50%) scale(0.95);
|
||
rotation: -2deg;
|
||
}
|
||
100% {
|
||
transform: translateX(-50%) scale(1);
|
||
opacity: 1;
|
||
rotation: 0deg;
|
||
}
|
||
}
|
||
|
||
.lapping-celebration {
|
||
animation: lappingPulse 1.5s ease-in-out;
|
||
transform-origin: center;
|
||
}
|
||
|
||
@keyframes lappingPulse {
|
||
0%, 100% { transform: scale(1) rotate(0deg); }
|
||
25% { transform: scale(1.3) rotate(-5deg); box-shadow: 0 0 25px #ffd700; }
|
||
50% { transform: scale(1.1) rotate(5deg); box-shadow: 0 0 35px #ffd700; }
|
||
75% { transform: scale(1.2) rotate(-3deg); box-shadow: 0 0 20px #ffd700; }
|
||
}
|
||
|
||
/* Race Countdown */
|
||
.race-countdown {
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
width: 100vw;
|
||
height: 100vh;
|
||
background: rgba(0, 0, 0, 0.8);
|
||
display: none;
|
||
justify-content: center;
|
||
align-items: center;
|
||
z-index: 10000;
|
||
backdrop-filter: blur(5px);
|
||
}
|
||
|
||
.countdown-display {
|
||
text-align: center;
|
||
color: white;
|
||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||
}
|
||
|
||
.countdown-number {
|
||
font-size: 15rem;
|
||
font-weight: 900;
|
||
line-height: 1;
|
||
text-shadow: 0 0 50px rgba(255, 255, 255, 0.5);
|
||
margin-bottom: 20px;
|
||
animation: countdownPulse 1s ease-out;
|
||
}
|
||
|
||
.countdown-text {
|
||
font-size: 2rem;
|
||
font-weight: 600;
|
||
margin-bottom: 40px;
|
||
opacity: 0.9;
|
||
}
|
||
|
||
.countdown-go {
|
||
font-size: 20rem;
|
||
font-weight: 900;
|
||
background: linear-gradient(135deg, #ff6b6b, #feca57, #48dbfb, #ff9ff3);
|
||
-webkit-background-clip: text;
|
||
-webkit-text-fill-color: transparent;
|
||
background-clip: text;
|
||
text-shadow: 0 0 50px rgba(255, 255, 255, 0.8);
|
||
animation: goAnimation 1s ease-out;
|
||
}
|
||
|
||
@keyframes countdownPulse {
|
||
0% {
|
||
transform: scale(0.5);
|
||
opacity: 0;
|
||
}
|
||
20% {
|
||
transform: scale(1.2);
|
||
opacity: 1;
|
||
}
|
||
100% {
|
||
transform: scale(1);
|
||
opacity: 1;
|
||
}
|
||
}
|
||
|
||
@keyframes goAnimation {
|
||
0% {
|
||
transform: scale(0.3) rotate(-10deg);
|
||
opacity: 0;
|
||
}
|
||
50% {
|
||
transform: scale(1.2) rotate(5deg);
|
||
opacity: 1;
|
||
}
|
||
100% {
|
||
transform: scale(1) rotate(0deg);
|
||
opacity: 1;
|
||
}
|
||
}
|
||
|
||
@keyframes rotate {
|
||
from { transform: rotate(0deg); }
|
||
to { transform: rotate(360deg); }
|
||
}
|
||
|
||
@keyframes success {
|
||
0% { transform: scale(0.8); opacity: 0; }
|
||
50% { transform: scale(1.1); }
|
||
100% { transform: scale(1); opacity: 1; }
|
||
}
|
||
|
||
@keyframes shake {
|
||
0%, 100% { transform: translateX(0); }
|
||
25% { transform: translateX(-5px); }
|
||
75% { transform: translateX(5px); }
|
||
}
|
||
|
||
/* Responsive Design */
|
||
@media (max-width: 768px) {
|
||
.equation-visual {
|
||
gap: 15px;
|
||
}
|
||
|
||
.challenge-number,
|
||
.answer-display,
|
||
.target-number {
|
||
font-size: 2.5rem;
|
||
padding: 15px 20px;
|
||
}
|
||
|
||
.answer-display {
|
||
min-width: 80px;
|
||
}
|
||
|
||
.equation-symbol {
|
||
font-size: 2rem;
|
||
}
|
||
|
||
}
|
||
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="container">
|
||
<!-- Beautiful Main Header -->
|
||
<header class="main-header">
|
||
<h1>Soroban Flashcards</h1>
|
||
<p>Interactive learning tools for mastering the Japanese abacus</p>
|
||
</header>
|
||
|
||
<!-- Navigation -->
|
||
<nav class="page-nav">
|
||
<div class="nav-buttons">
|
||
<button class="nav-btn active" data-section="welcome">🏠 Welcome</button>
|
||
<button class="nav-btn" data-section="guide">📚 Complete Guide</button>
|
||
<button class="nav-btn" data-section="configuration">⚙️ Configuration</button>
|
||
<button class="nav-btn" data-section="challenges">🎯 Challenges</button>
|
||
<button class="nav-btn" data-section="flashcards">📊 Practice</button>
|
||
</div>
|
||
</nav>
|
||
|
||
<!-- Content Area -->
|
||
<div class="content-area">
|
||
|
||
<!-- Section 1: Welcome -->
|
||
<section id="welcome" class="page-section active">
|
||
<div class="welcome-hero">
|
||
<div class="welcome-content">
|
||
<h1 class="welcome-title">🏠 Welcome to Soroban Mastery</h1>
|
||
<p class="welcome-subtitle">Your complete interactive learning platform for mastering the Japanese abacus</p>
|
||
|
||
<div class="feature-grid">
|
||
<div class="feature-card">
|
||
<div class="feature-icon">📚</div>
|
||
<h3>Complete Guide</h3>
|
||
<p>Learn to read numbers and perform arithmetic operations with step-by-step visual tutorials</p>
|
||
<button class="feature-btn" data-section="guide">Start Learning →</button>
|
||
</div>
|
||
|
||
<div class="feature-card">
|
||
<div class="feature-icon">🎯</div>
|
||
<h3>Interactive Challenges</h3>
|
||
<p>Test your skills with engaging games including speed reading challenges and pattern matching</p>
|
||
<button class="feature-btn" data-section="challenges">Play Games →</button>
|
||
</div>
|
||
|
||
<div class="feature-card">
|
||
<div class="feature-icon">📊</div>
|
||
<h3>Practice Flashcards</h3>
|
||
<p>Master number recognition with beautiful, customizable flashcards and printable PDF sets</p>
|
||
<button class="feature-btn" data-section="flashcards">Practice Now →</button>
|
||
</div>
|
||
|
||
<div class="feature-card">
|
||
<div class="feature-icon">⚙️</div>
|
||
<h3>Customization</h3>
|
||
<p>Personalize your learning experience with different color schemes, card layouts, and difficulty levels</p>
|
||
<button class="feature-btn" data-section="configuration">Customize →</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="quick-start">
|
||
<h2>Quick Start Guide</h2>
|
||
<div class="steps">
|
||
<div class="step">
|
||
<div class="step-number">1</div>
|
||
<div class="step-content">
|
||
<h4>Learn the Basics</h4>
|
||
<p>Start with our complete guide to understand how to read numbers on the soroban</p>
|
||
</div>
|
||
</div>
|
||
<div class="step">
|
||
<div class="step-number">2</div>
|
||
<div class="step-content">
|
||
<h4>Practice Recognition</h4>
|
||
<p>Use interactive flashcards to build speed and accuracy in number recognition</p>
|
||
</div>
|
||
</div>
|
||
<div class="step">
|
||
<div class="step-number">3</div>
|
||
<div class="step-content">
|
||
<h4>Challenge Yourself</h4>
|
||
<p>Test your skills with timed challenges and competitive games</p>
|
||
</div>
|
||
</div>
|
||
<div class="step">
|
||
<div class="step-number">4</div>
|
||
<div class="step-content">
|
||
<h4>Master Arithmetic</h4>
|
||
<p>Learn advanced techniques for addition, subtraction, and more complex operations</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="stats-preview">
|
||
<h3>What You Will Learn</h3>
|
||
<div class="stats-grid">
|
||
<div class="stat-item">
|
||
<div class="stat-number">50</div>
|
||
<div class="stat-label">Practice Numbers</div>
|
||
</div>
|
||
<div class="stat-item">
|
||
<div class="stat-number">4</div>
|
||
<div class="stat-label">Game Modes</div>
|
||
</div>
|
||
<div class="stat-item">
|
||
<div class="stat-number">5</div>
|
||
<div class="stat-label">Color Schemes</div>
|
||
</div>
|
||
<div class="stat-item">
|
||
<div class="stat-number">∞</div>
|
||
<div class="stat-label">Skill Levels</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- Section 2: Complete Guide -->
|
||
<section id="guide" class="page-section">
|
||
<div class="section-header">
|
||
<h2 class="section-title">📚 Complete Soroban Mastery Guide</h2>
|
||
<p class="section-subtitle">From basic reading to advanced arithmetic - everything you need to master the Japanese abacus</p>
|
||
</div>
|
||
|
||
<!-- Guide Navigation -->
|
||
<div class="guide-nav">
|
||
<div class="guide-nav-buttons">
|
||
<button class="guide-nav-btn active" data-guide-section="reading">📖 Reading Numbers</button>
|
||
<button class="guide-nav-btn" data-guide-section="arithmetic">🧮 Arithmetic Operations</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Guide Content -->
|
||
<div class="guide-content">
|
||
|
||
<!-- Reading Numbers Section -->
|
||
<div id="reading" class="guide-section active">
|
||
|
||
<!-- Step 1: Basic Structure -->
|
||
<div class="tutorial-step">
|
||
<div class="step-header">
|
||
<h3><span class="step-number">1</span>Understanding the Structure</h3>
|
||
</div>
|
||
<div class="step-content">
|
||
<div class="tutorial-grid">
|
||
<div class="tutorial-text">
|
||
<p>The soroban has <strong>heaven beads</strong> (top) and <strong>earth beads</strong> (bottom) separated by a horizontal bar.</p>
|
||
<ul>
|
||
<li><strong>Heaven bead:</strong> Worth 5 units</li>
|
||
<li><strong>Earth beads:</strong> Worth 1 unit each</li>
|
||
<li><strong>Active position:</strong> Beads pushed toward the bar</li>
|
||
</ul>
|
||
</div>
|
||
<div class="tutorial-visual">
|
||
<div class="example-abacus" id="tutorial-structure">
|
||
<svg class="typst-doc" viewBox="0 0 108 108" width="108pt" height="108pt" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:h5="http://www.w3.org/1999/xhtml">
|
||
<g>
|
||
<g transform="translate(5.400000000000001 5.400000000000001)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(36.1 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 -11.400000000000002)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(11 10)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 79.5 L 3 79.5 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(6.5 10)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 40)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 52.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 65)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 77.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(0 30)">
|
||
<path class="typst-shape" fill="#000000" fill-rule="nonzero" d="M 0 0 L 0 2 L 25 2 L 25 0 Z "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</svg>
|
||
</div>
|
||
<div class="example-label">Zero (0) - All beads away from bar</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Step 2: Single Digits -->
|
||
<div class="tutorial-step">
|
||
<div class="step-header">
|
||
<h3><span class="step-number">2</span>Reading Single Digits (1-9)</h3>
|
||
</div>
|
||
<div class="step-content">
|
||
<div class="examples-grid">
|
||
<div class="example-item">
|
||
<div class="example-abacus"><svg class="typst-doc" viewBox="0 0 108 108" width="108pt" height="108pt" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:h5="http://www.w3.org/1999/xhtml">
|
||
<g>
|
||
<g transform="translate(5.400000000000001 5.400000000000001)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(36.1 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 -11.400000000000002)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(11 10)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 80 L 3 80 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(6.5 10)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 33)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 53)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 65.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 78)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(0 30)">
|
||
<path class="typst-shape" fill="#000000" fill-rule="nonzero" d="M 0 0 L 0 2 L 25 2 L 25 0 Z "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</svg></div>
|
||
<div class="example-label">1 = One earth bead up</div>
|
||
</div>
|
||
<div class="example-item">
|
||
<div class="example-abacus"><svg class="typst-doc" viewBox="0 0 108 108" width="108pt" height="108pt" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:h5="http://www.w3.org/1999/xhtml">
|
||
<g>
|
||
<g transform="translate(5.400000000000001 5.400000000000001)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(36.1 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 -11.400000000000002)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(11 10)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 80 L 3 80 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(6.5 10)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 33)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 45.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 58)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 78)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(0 30)">
|
||
<path class="typst-shape" fill="#000000" fill-rule="nonzero" d="M 0 0 L 0 2 L 25 2 L 25 0 Z "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</svg></div>
|
||
<div class="example-label">3 = Three earth beads up</div>
|
||
</div>
|
||
<div class="example-item">
|
||
<div class="example-abacus"><svg class="typst-doc" viewBox="0 0 108 108" width="108pt" height="108pt" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:h5="http://www.w3.org/1999/xhtml">
|
||
<g>
|
||
<g transform="translate(5.400000000000001 5.400000000000001)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(36.1 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 -11.400000000000002)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(11 17)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 72.5 L 3 72.5 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(6.5 17)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 40)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 52.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 65)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 77.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(0 30)">
|
||
<path class="typst-shape" fill="#000000" fill-rule="nonzero" d="M 0 0 L 0 2 L 25 2 L 25 0 Z "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</svg></div>
|
||
<div class="example-label">5 = Heaven bead down</div>
|
||
</div>
|
||
<div class="example-item">
|
||
<div class="example-abacus"><svg class="typst-doc" viewBox="0 0 108 108" width="108pt" height="108pt" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:h5="http://www.w3.org/1999/xhtml">
|
||
<g>
|
||
<g transform="translate(5.400000000000001 5.400000000000001)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(36.1 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 -11.400000000000002)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(11 17)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 73 L 3 73 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(6.5 17)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 33)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 45.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 65.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 78)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(0 30)">
|
||
<path class="typst-shape" fill="#000000" fill-rule="nonzero" d="M 0 0 L 0 2 L 25 2 L 25 0 Z "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</svg></div>
|
||
<div class="example-label">7 = Heaven bead + 2 earth beads</div>
|
||
</div>
|
||
<div class="example-item">
|
||
<div class="example-abacus"><svg class="typst-doc" viewBox="0 0 108 108" width="108pt" height="108pt" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:h5="http://www.w3.org/1999/xhtml">
|
||
<g>
|
||
<g transform="translate(5.400000000000001 5.400000000000001)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(36.1 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 -11.400000000000002)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(11 17)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 65.5 L 3 65.5 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(6.5 17)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 33)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 45.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 58)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 70.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(0 30)">
|
||
<path class="typst-shape" fill="#000000" fill-rule="nonzero" d="M 0 0 L 0 2 L 25 2 L 25 0 Z "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</svg></div>
|
||
<div class="example-label">9 = Heaven bead + 4 earth beads</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Step 3: Multi-digit Numbers -->
|
||
<div class="tutorial-step">
|
||
<div class="step-header">
|
||
<h3><span class="step-number">3</span>Multi-Digit Numbers</h3>
|
||
</div>
|
||
<div class="step-content">
|
||
<div class="tutorial-text">
|
||
<p>Each column represents a place value. Read from right to left: <strong>ones, tens, hundreds, thousands</strong>.</p>
|
||
</div>
|
||
<div class="examples-grid">
|
||
<div class="example-item">
|
||
<div class="example-abacus"><svg class="typst-doc" viewBox="0 0 108 108" width="108pt" height="108pt" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:h5="http://www.w3.org/1999/xhtml">
|
||
<g>
|
||
<g transform="translate(5.400000000000001 5.400000000000001)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(23.599999999999998 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 -11.400000000000002)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(11 10)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 80 L 3 80 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(6.5 10)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 33)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 45.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 65.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 78)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(36 10)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 80 L 3 80 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(31.5 10)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 33)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 45.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 58)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 78)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(0 30)">
|
||
<path class="typst-shape" fill="#000000" fill-rule="nonzero" d="M 0 0 L 0 2 L 50 2 L 50 0 Z "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</svg></div>
|
||
<div class="example-label">23 = 2 tens + 3 ones</div>
|
||
<div class="example-breakdown">Tens: 2 | Ones: 3</div>
|
||
</div>
|
||
<div class="example-item">
|
||
<div class="example-abacus"><svg class="typst-doc" viewBox="0 0 108 108" width="108pt" height="108pt" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:h5="http://www.w3.org/1999/xhtml">
|
||
<g>
|
||
<g transform="translate(5.400000000000001 5.400000000000001)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(23.599999999999998 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 -11.400000000000002)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(11 17)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 73 L 3 73 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(6.5 17)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 33)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 53)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 65.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 78)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(36 17)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 73 L 3 73 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(31.5 17)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 33)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 45.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 65.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 78)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(0 30)">
|
||
<path class="typst-shape" fill="#000000" fill-rule="nonzero" d="M 0 0 L 0 2 L 50 2 L 50 0 Z "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</svg></div>
|
||
<div class="example-label">67 = 6 tens + 7 ones</div>
|
||
<div class="example-breakdown">Tens: 6 | Ones: 7</div>
|
||
</div>
|
||
<div class="example-item">
|
||
<div class="example-abacus"><svg class="typst-doc" viewBox="0 0 108 108" width="108pt" height="108pt" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:h5="http://www.w3.org/1999/xhtml">
|
||
<g>
|
||
<g transform="translate(5.400000000000001 5.400000000000001)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(11.099999999999998 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 -11.400000000000002)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(11 10)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 80 L 3 80 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(6.5 10)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 33)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#f18f01" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 53)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 65.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 78)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(36 17)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 72.5 L 3 72.5 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(31.5 17)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 40)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 52.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 65)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 77.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(61 17)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 73 L 3 73 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(56.5 17)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(56.5 33)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(56.5 45.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(56.5 58)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(56.5 78)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(0 30)">
|
||
<path class="typst-shape" fill="#000000" fill-rule="nonzero" d="M 0 0 L 0 2 L 75 2 L 75 0 Z "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</svg></div>
|
||
<div class="example-label">158 = 1 hundred + 5 tens + 8 ones</div>
|
||
<div class="example-breakdown">Hundreds: 1 | Tens: 5 | Ones: 8</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Step 4: Practice Tips -->
|
||
<div class="tutorial-step">
|
||
<div class="step-header">
|
||
<h3><span class="step-number">4</span>Practice Strategy</h3>
|
||
</div>
|
||
<div class="step-content">
|
||
<div class="tutorial-grid">
|
||
<div class="tutorial-text">
|
||
<h4>Start Simple, Build Complexity</h4>
|
||
<ol>
|
||
<li>Master single digits (0-9) first</li>
|
||
<li>Practice two-digit numbers (10-99)</li>
|
||
<li>Advance to three-digit numbers (100-999)</li>
|
||
<li>Challenge yourself with larger numbers</li>
|
||
</ol>
|
||
|
||
<h4>Reading Strategy</h4>
|
||
<p>For each column, count the active beads:</p>
|
||
<ul>
|
||
<li>Heaven bead down = add 5</li>
|
||
<li>Earth beads up = add 1 each</li>
|
||
<li>Total the values for each column</li>
|
||
</ul>
|
||
</div>
|
||
<div class="tutorial-visual">
|
||
<div class="example-abacus" id="practice-example"><svg class="typst-doc" viewBox="0 0 108 108" width="108pt" height="108pt" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:h5="http://www.w3.org/1999/xhtml">
|
||
<g>
|
||
<g transform="translate(5.400000000000001 5.400000000000001)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(11.099999999999998 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 -11.400000000000002)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(11 10)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 72.5 L 3 72.5 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(6.5 10)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 33)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#f18f01" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 45.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#f18f01" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 58)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#f18f01" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 70.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#f18f01" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(36 17)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 72.5 L 3 72.5 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(31.5 17)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 40)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 52.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 65)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 77.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(61 17)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 73 L 3 73 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(56.5 17)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(56.5 33)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(56.5 53)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(56.5 65.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(56.5 78)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(0 30)">
|
||
<path class="typst-shape" fill="#000000" fill-rule="nonzero" d="M 0 0 L 0 2 L 75 2 L 75 0 Z "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</svg></div>
|
||
<div class="example-label" id="practice-label">Practice Number</div>
|
||
<div class="practice-hint">Try to read this number before revealing!</div>
|
||
<button class="reveal-btn" id="reveal-answer" onclick="revealAnswer()">Reveal Answer</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
</div> <!-- End Reading Section -->
|
||
|
||
<!-- Arithmetic Operations Section -->
|
||
<div id="arithmetic" class="guide-section">
|
||
|
||
<div class="section-intro">
|
||
<h3>🧮 Soroban Arithmetic Mastery</h3>
|
||
<p>Learn traditional Japanese calculation techniques step-by-step with visual examples</p>
|
||
</div>
|
||
|
||
<!-- Addition Section -->
|
||
<div class="tutorial-step">
|
||
<div class="step-header">
|
||
<h3><span class="step-number">+</span>Addition Techniques</h3>
|
||
</div>
|
||
<div class="step-content">
|
||
<div class="arithmetic-intro">
|
||
<p>Addition on the soroban follows specific rules for manipulating beads efficiently. Master these fundamental patterns:</p>
|
||
</div>
|
||
|
||
<!-- Simple Addition -->
|
||
<div class="operation-section">
|
||
<h4>Simple Addition (No Carries)</h4>
|
||
<div class="calculation-example">
|
||
<div class="calc-step">
|
||
<div class="step-description">
|
||
<h5>Example: 23 + 14 = 37</h5>
|
||
<p>Add ones first, then tens. No carrying required.</p>
|
||
</div>
|
||
<div class="calc-visual-sequence">
|
||
<div class="calc-frame">
|
||
<div class="frame-label">Start: 23</div>
|
||
<div class="example-abacus"><svg class="typst-doc" viewBox="0 0 108 108" width="108pt" height="108pt" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:h5="http://www.w3.org/1999/xhtml">
|
||
<g>
|
||
<g transform="translate(5.400000000000001 5.400000000000001)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(23.599999999999998 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 -11.400000000000002)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(11 10)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 80 L 3 80 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(6.5 10)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 33)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 45.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 65.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 78)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(36 10)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 80 L 3 80 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(31.5 10)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 33)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 45.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 58)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 78)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(0 30)">
|
||
<path class="typst-shape" fill="#000000" fill-rule="nonzero" d="M 0 0 L 0 2 L 50 2 L 50 0 Z "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</svg></div>
|
||
</div>
|
||
<div class="calc-arrow">+4 ones →</div>
|
||
<div class="calc-frame">
|
||
<div class="frame-label">Add 4 to ones: 27</div>
|
||
<div class="example-abacus"><svg class="typst-doc" viewBox="0 0 108 108" width="108pt" height="108pt" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:h5="http://www.w3.org/1999/xhtml">
|
||
<g>
|
||
<g transform="translate(5.400000000000001 5.400000000000001)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(23.599999999999998 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 -11.400000000000002)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(11 10)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 80 L 3 80 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(6.5 10)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 33)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 45.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 65.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 78)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(36 17)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 73 L 3 73 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(31.5 17)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 33)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 45.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 65.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 78)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(0 30)">
|
||
<path class="typst-shape" fill="#000000" fill-rule="nonzero" d="M 0 0 L 0 2 L 50 2 L 50 0 Z "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</svg></div>
|
||
</div>
|
||
<div class="calc-arrow">+1 ten →</div>
|
||
<div class="calc-frame">
|
||
<div class="frame-label">Final: 37</div>
|
||
<div class="example-abacus"><svg class="typst-doc" viewBox="0 0 108 108" width="108pt" height="108pt" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:h5="http://www.w3.org/1999/xhtml">
|
||
<g>
|
||
<g transform="translate(5.400000000000001 5.400000000000001)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(23.599999999999998 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 -11.400000000000002)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(11 10)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 80 L 3 80 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(6.5 10)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 33)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 45.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 58)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 78)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(36 17)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 73 L 3 73 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(31.5 17)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 33)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 45.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 65.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 78)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(0 30)">
|
||
<path class="typst-shape" fill="#000000" fill-rule="nonzero" d="M 0 0 L 0 2 L 50 2 L 50 0 Z "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</svg></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Addition with Carry -->
|
||
<div class="operation-section">
|
||
<h4>Addition with Carrying</h4>
|
||
<div class="calculation-example">
|
||
<div class="calc-step">
|
||
<div class="step-description">
|
||
<h5>Example: 47 + 28 = 75</h5>
|
||
<p>When ones exceed 9, carry to tens column.</p>
|
||
<ol class="technique-steps">
|
||
<li>Start with 47 on the abacus</li>
|
||
<li>Add 8 to ones: 7 + 8 = 15 (carry 1)</li>
|
||
<li>Place 5 in ones, carry 1 to tens</li>
|
||
<li>Add 2 + 1 (carry) = 3 to tens: 4 + 3 = 7</li>
|
||
</ol>
|
||
</div>
|
||
<div class="calc-visual-sequence">
|
||
<div class="calc-frame">
|
||
<div class="frame-label">Start: 47</div>
|
||
<div class="example-abacus"><svg class="typst-doc" viewBox="0 0 108 108" width="108pt" height="108pt" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:h5="http://www.w3.org/1999/xhtml">
|
||
<g>
|
||
<g transform="translate(5.400000000000001 5.400000000000001)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(23.599999999999998 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 -11.400000000000002)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(11 10)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 72.5 L 3 72.5 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(6.5 10)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 33)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 45.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 58)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 70.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(36 17)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 73 L 3 73 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(31.5 17)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 33)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 45.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 65.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 78)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(0 30)">
|
||
<path class="typst-shape" fill="#000000" fill-rule="nonzero" d="M 0 0 L 0 2 L 50 2 L 50 0 Z "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</svg></div>
|
||
</div>
|
||
<div class="calc-arrow">+8 ones<br>(7+8=15) →</div>
|
||
<div class="calc-frame">
|
||
<div class="frame-label">Carry: 55</div>
|
||
<div class="example-abacus"><svg class="typst-doc" viewBox="0 0 108 108" width="108pt" height="108pt" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:h5="http://www.w3.org/1999/xhtml">
|
||
<g>
|
||
<g transform="translate(5.400000000000001 5.400000000000001)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(23.599999999999998 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 -11.400000000000002)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(11 17)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 72.5 L 3 72.5 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(6.5 17)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 40)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 52.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 65)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 77.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(36 17)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 72.5 L 3 72.5 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(31.5 17)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 40)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 52.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 65)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 77.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(0 30)">
|
||
<path class="typst-shape" fill="#000000" fill-rule="nonzero" d="M 0 0 L 0 2 L 50 2 L 50 0 Z "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</svg></div>
|
||
</div>
|
||
<div class="calc-arrow">+2 tens →</div>
|
||
<div class="calc-frame">
|
||
<div class="frame-label">Final: 75</div>
|
||
<div class="example-abacus"><svg class="typst-doc" viewBox="0 0 108 108" width="108pt" height="108pt" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:h5="http://www.w3.org/1999/xhtml">
|
||
<g>
|
||
<g transform="translate(5.400000000000001 5.400000000000001)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(23.599999999999998 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 -11.400000000000002)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(11 17)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 73 L 3 73 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(6.5 17)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 33)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 45.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 65.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 78)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(36 17)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 72.5 L 3 72.5 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(31.5 17)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 40)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 52.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 65)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 77.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(0 30)">
|
||
<path class="typst-shape" fill="#000000" fill-rule="nonzero" d="M 0 0 L 0 2 L 50 2 L 50 0 Z "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</svg></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Subtraction Section -->
|
||
<div class="tutorial-step">
|
||
<div class="step-header">
|
||
<h3><span class="step-number">−</span>Subtraction Techniques</h3>
|
||
</div>
|
||
<div class="step-content">
|
||
<div class="arithmetic-intro">
|
||
<p>Subtraction requires understanding complement methods and borrowing. These are traditional soroban techniques.</p>
|
||
</div>
|
||
|
||
<!-- Simple Subtraction -->
|
||
<div class="operation-section">
|
||
<h4>Simple Subtraction (No Borrowing)</h4>
|
||
<div class="calculation-example">
|
||
<div class="calc-step">
|
||
<div class="step-description">
|
||
<h5>Example: 89 - 34 = 55</h5>
|
||
<p>Subtract ones first, then tens. No borrowing needed.</p>
|
||
</div>
|
||
<div class="calc-visual-sequence">
|
||
<div class="calc-frame">
|
||
<div class="frame-label">Start: 89</div>
|
||
<div class="example-abacus"><svg class="typst-doc" viewBox="0 0 108 108" width="108pt" height="108pt" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:h5="http://www.w3.org/1999/xhtml">
|
||
<g>
|
||
<g transform="translate(5.400000000000001 5.400000000000001)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(23.599999999999998 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 -11.400000000000002)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(11 17)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 73 L 3 73 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(6.5 17)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 33)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 45.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 58)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 78)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(36 17)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 65.5 L 3 65.5 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(31.5 17)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 33)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 45.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 58)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 70.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(0 30)">
|
||
<path class="typst-shape" fill="#000000" fill-rule="nonzero" d="M 0 0 L 0 2 L 50 2 L 50 0 Z "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</svg></div>
|
||
</div>
|
||
<div class="calc-arrow">-4 ones →</div>
|
||
<div class="calc-frame">
|
||
<div class="frame-label">After -4: 85</div>
|
||
<div class="example-abacus"><svg class="typst-doc" viewBox="0 0 108 108" width="108pt" height="108pt" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:h5="http://www.w3.org/1999/xhtml">
|
||
<g>
|
||
<g transform="translate(5.400000000000001 5.400000000000001)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(23.599999999999998 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 -11.400000000000002)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(11 17)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 73 L 3 73 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(6.5 17)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 33)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 45.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 58)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 78)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(36 17)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 72.5 L 3 72.5 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(31.5 17)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 40)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 52.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 65)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 77.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(0 30)">
|
||
<path class="typst-shape" fill="#000000" fill-rule="nonzero" d="M 0 0 L 0 2 L 50 2 L 50 0 Z "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</svg></div>
|
||
</div>
|
||
<div class="calc-arrow">-3 tens →</div>
|
||
<div class="calc-frame">
|
||
<div class="frame-label">Final: 55</div>
|
||
<div class="example-abacus"><svg class="typst-doc" viewBox="0 0 108 108" width="108pt" height="108pt" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:h5="http://www.w3.org/1999/xhtml">
|
||
<g>
|
||
<g transform="translate(5.400000000000001 5.400000000000001)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(23.599999999999998 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 -11.400000000000002)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(11 17)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 72.5 L 3 72.5 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(6.5 17)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 40)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 52.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 65)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 77.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(36 17)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 72.5 L 3 72.5 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(31.5 17)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 40)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 52.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 65)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 77.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(0 30)">
|
||
<path class="typst-shape" fill="#000000" fill-rule="nonzero" d="M 0 0 L 0 2 L 50 2 L 50 0 Z "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</svg></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Complement Method -->
|
||
<div class="operation-section">
|
||
<h4>Complement Method (Advanced)</h4>
|
||
<div class="technique-box">
|
||
<h5>Five Complement Rule</h5>
|
||
<p>When subtracting from 5, use complement pairs:</p>
|
||
<div class="complement-grid">
|
||
<div class="complement-pair">5 - 1 = 4 → Remove heaven, add 4 earth</div>
|
||
<div class="complement-pair">5 - 2 = 3 → Remove heaven, add 3 earth</div>
|
||
<div class="complement-pair">5 - 3 = 2 → Remove heaven, add 2 earth</div>
|
||
<div class="complement-pair">5 - 4 = 1 → Remove heaven, add 1 earth</div>
|
||
</div>
|
||
</div>
|
||
<div class="technique-box">
|
||
<h5>Ten Complement Rule</h5>
|
||
<p>When borrowing from next column:</p>
|
||
<div class="complement-grid">
|
||
<div class="complement-pair">10 - 1 = 9 → Borrow 1, add 9</div>
|
||
<div class="complement-pair">10 - 7 = 3 → Borrow 1, add 3</div>
|
||
<div class="complement-pair">10 - 8 = 2 → Borrow 1, add 2</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Multiplication Section -->
|
||
<div class="tutorial-step">
|
||
<div class="step-header">
|
||
<h3><span class="step-number">×</span>Multiplication Methods</h3>
|
||
</div>
|
||
<div class="step-content">
|
||
<div class="arithmetic-intro">
|
||
<p>Soroban multiplication uses repeated addition and special column techniques for efficiency.</p>
|
||
</div>
|
||
|
||
<!-- Single Digit Multiplication -->
|
||
<div class="operation-section">
|
||
<h4>Single-Digit Multiplication</h4>
|
||
<div class="calculation-example">
|
||
<div class="calc-step">
|
||
<div class="step-description">
|
||
<h5>Example: 23 × 4 = 92</h5>
|
||
<p>Multiply each digit by 4, handling carries properly.</p>
|
||
<ol class="technique-steps">
|
||
<li>Set up 23 on the left, multiply by 4</li>
|
||
<li>3 × 4 = 12 → Write 2, carry 1</li>
|
||
<li>2 × 4 = 8, plus carry 1 = 9</li>
|
||
<li>Result: 92</li>
|
||
</ol>
|
||
</div>
|
||
<div class="mult-layout">
|
||
<div class="mult-setup">
|
||
<div class="mult-label">Setup: 23 × 4</div>
|
||
<div class="example-abacus"><svg class="typst-doc" viewBox="0 0 108 108" width="108pt" height="108pt" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:h5="http://www.w3.org/1999/xhtml">
|
||
<g>
|
||
<g transform="translate(5.400000000000001 5.400000000000001)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(23.599999999999998 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 -11.400000000000002)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(11 10)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 80 L 3 80 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(6.5 10)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 33)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 45.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 65.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 78)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(36 10)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 80 L 3 80 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(31.5 10)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 33)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 45.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 58)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 78)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(0 30)">
|
||
<path class="typst-shape" fill="#000000" fill-rule="nonzero" d="M 0 0 L 0 2 L 50 2 L 50 0 Z "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</svg></div>
|
||
</div>
|
||
<div class="mult-result">
|
||
<div class="mult-label">Result: 92</div>
|
||
<div class="example-abacus"><svg class="typst-doc" viewBox="0 0 108 108" width="108pt" height="108pt" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:h5="http://www.w3.org/1999/xhtml">
|
||
<g>
|
||
<g transform="translate(5.400000000000001 5.400000000000001)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(23.599999999999998 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 -11.400000000000002)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(11 17)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 65.5 L 3 65.5 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(6.5 17)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 33)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 45.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 58)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 70.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(36 10)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 80 L 3 80 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(31.5 10)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 33)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 45.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 65.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 78)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(0 30)">
|
||
<path class="typst-shape" fill="#000000" fill-rule="nonzero" d="M 0 0 L 0 2 L 50 2 L 50 0 Z "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</svg></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Traditional Method -->
|
||
<div class="operation-section">
|
||
<h4>Traditional Multiplication Layout</h4>
|
||
<div class="technique-box">
|
||
<h5>Column Arrangement</h5>
|
||
<p>Traditional soroban multiplication uses specific column arrangements:</p>
|
||
<ul>
|
||
<li><strong>Multiplicand:</strong> Left side of abacus</li>
|
||
<li><strong>Multiplier:</strong> Right side of abacus</li>
|
||
<li><strong>Product:</strong> Center columns for result</li>
|
||
<li><strong>Working space:</strong> Extra columns for carries</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Division Section -->
|
||
<div class="tutorial-step">
|
||
<div class="step-header">
|
||
<h3><span class="step-number">÷</span>Division Techniques</h3>
|
||
</div>
|
||
<div class="step-content">
|
||
<div class="arithmetic-intro">
|
||
<p>Division on soroban uses traditional algorithms with quotient estimation and remainder handling.</p>
|
||
</div>
|
||
|
||
<!-- Simple Division -->
|
||
<div class="operation-section">
|
||
<h4>Simple Division</h4>
|
||
<div class="calculation-example">
|
||
<div class="calc-step">
|
||
<div class="step-description">
|
||
<h5>Example: 84 ÷ 4 = 21</h5>
|
||
<p>Divide left to right, estimating quotient digits.</p>
|
||
<ol class="technique-steps">
|
||
<li>Set dividend 84 on left side</li>
|
||
<li>8 ÷ 4 = 2 → Place 2 in quotient</li>
|
||
<li>4 ÷ 4 = 1 → Place 1 in quotient</li>
|
||
<li>Result: 21</li>
|
||
</ol>
|
||
</div>
|
||
<div class="div-layout">
|
||
<div class="div-setup">
|
||
<div class="div-label">Dividend: 84</div>
|
||
<div class="example-abacus"><svg class="typst-doc" viewBox="0 0 108 108" width="108pt" height="108pt" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:h5="http://www.w3.org/1999/xhtml">
|
||
<g>
|
||
<g transform="translate(5.400000000000001 5.400000000000001)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(23.599999999999998 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 -11.400000000000002)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(11 17)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 73 L 3 73 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(6.5 17)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 33)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 45.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 58)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 78)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(36 10)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 72.5 L 3 72.5 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(31.5 10)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 33)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 45.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 58)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 70.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(0 30)">
|
||
<path class="typst-shape" fill="#000000" fill-rule="nonzero" d="M 0 0 L 0 2 L 50 2 L 50 0 Z "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</svg></div>
|
||
</div>
|
||
<div class="div-result">
|
||
<div class="div-label">Quotient: 21</div>
|
||
<div class="example-abacus"><svg class="typst-doc" viewBox="0 0 108 108" width="108pt" height="108pt" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:h5="http://www.w3.org/1999/xhtml">
|
||
<g>
|
||
<g transform="translate(5.400000000000001 5.400000000000001)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(23.599999999999998 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 -11.400000000000002)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(11 10)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 80 L 3 80 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(6.5 10)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 33)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 45.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 65.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 78)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(36 10)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 80 L 3 80 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(31.5 10)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 33)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 53)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 65.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 78)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(0 30)">
|
||
<path class="typst-shape" fill="#000000" fill-rule="nonzero" d="M 0 0 L 0 2 L 50 2 L 50 0 Z "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</svg></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Division Layout -->
|
||
<div class="operation-section">
|
||
<h4>Traditional Division Layout</h4>
|
||
<div class="technique-box">
|
||
<h5>Column Organization</h5>
|
||
<ul>
|
||
<li><strong>Dividend:</strong> Starting on left columns</li>
|
||
<li><strong>Divisor:</strong> Held in memory or right side</li>
|
||
<li><strong>Quotient:</strong> Built up in designated columns</li>
|
||
<li><strong>Remainder:</strong> Left in dividend columns</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Practice Tips -->
|
||
<div class="tutorial-step">
|
||
<div class="step-header">
|
||
<h3><span class="step-number">💡</span>Mastery Tips</h3>
|
||
</div>
|
||
<div class="step-content">
|
||
<div class="tips-grid">
|
||
<div class="tip-card">
|
||
<h4>🎯 Accuracy First</h4>
|
||
<p>Master accuracy before attempting speed. Slow, correct movements build muscle memory for rapid calculation.</p>
|
||
</div>
|
||
<div class="tip-card">
|
||
<h4>🔄 Practice Complements</h4>
|
||
<p>Memorize 5-complements (1-4, 2-3) and 10-complements (1-9, 2-8, 3-7, 4-6) for efficient subtraction.</p>
|
||
</div>
|
||
<div class="tip-card">
|
||
<h4>👁️ Visualization</h4>
|
||
<p>Learn to see numbers as bead patterns. This enables mental soroban calculation without the physical device.</p>
|
||
</div>
|
||
<div class="tip-card">
|
||
<h4>📚 Progressive Learning</h4>
|
||
<p>Master single digits → two digits → three digits. Build complexity gradually for solid foundation.</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
</div> <!-- End Arithmetic Section -->
|
||
|
||
</div> <!-- End Guide Content -->
|
||
</section>
|
||
|
||
<!-- Section 2: Configuration -->
|
||
<section id="configuration" class="page-section">
|
||
<div class="section-header">
|
||
<h2 class="section-title">⚙️ Current Configuration</h2>
|
||
<p class="section-subtitle">Settings used for this flashcard set</p>
|
||
</div>
|
||
|
||
<div class="config-showcase">
|
||
<div class="config-item">
|
||
<div class="config-label"><strong>📊 Cards Generated:</strong></div>
|
||
<div class="config-value">50 flashcards</div>
|
||
</div>
|
||
|
||
<div class="config-item">
|
||
<div class="config-label"><strong>🔢 Number Range:</strong></div>
|
||
<div class="config-value">1 - 50</div>
|
||
<div class="config-description">Learn numbers from 1 - 50 with authentic soroban bead patterns</div>
|
||
</div>
|
||
|
||
<div class="config-item">
|
||
<div class="config-label"><strong>🎨 Color Scheme:</strong></div>
|
||
<div class="config-value">Each place value (ones, tens, hundreds) has a different color</div>
|
||
<div class="color-palette">
|
||
<div class="color-sample heaven" title="Heaven beads (5s)"></div>
|
||
<div class="color-sample earth" title="Earth beads (1s)"></div>
|
||
<div class="color-sample frame" title="Frame"></div>
|
||
</div>
|
||
<div class="config-description">Colors help distinguish between heaven beads (worth 5) and earth beads (worth 1)</div>
|
||
</div>
|
||
|
||
<div class="config-item">
|
||
<div class="config-label"><strong>⚪ Bead Shape:</strong></div>
|
||
<div class="config-value" style="display: flex; align-items: center; gap: 15px;">
|
||
Circle
|
||
<div class="bead-examples">
|
||
<div class="bead-sample" data-shape="Circle" title="Current bead shape"></div>
|
||
<div class="shape-label">Circle beads</div>
|
||
</div>
|
||
</div>
|
||
<div class="config-description">Visual style affects readability - choose what works best for you</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="offline-info">
|
||
<h3>📱 Offline Ready</h3>
|
||
<p>This is a complete, self-contained web page that works without an internet connection. All interactive games, sound effects, and features are built-in. Save this file locally or bookmark it for offline practice anywhere!</p>
|
||
|
||
<div class="tech-features">
|
||
<div class="feature">✨ Pure HTML/CSS/JavaScript</div>
|
||
<div class="feature">🎵 Generated sound effects</div>
|
||
<div class="feature">🎮 Interactive games built-in</div>
|
||
<div class="feature">📲 Works on any device</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- Section 3: Challenges -->
|
||
<section id="challenges" class="page-section">
|
||
<div class="section-header">
|
||
<h2 class="section-title">🎯 Interactive Challenges</h2>
|
||
<p class="section-subtitle">Test your soroban skills with engaging games and quizzes</p>
|
||
</div>
|
||
|
||
<div class="challenges-section">
|
||
<!-- Tag Filter Bar -->
|
||
<div class="tag-filter-bar">
|
||
<div class="filter-title">Filter by Tags:</div>
|
||
<div class="tag-filters">
|
||
<button class="tag-filter active" data-tag="all">All Games <span class="tag-count">(4)</span></button>
|
||
<button class="tag-filter" data-tag="memory">🔍 Memory <span class="tag-count">(2)</span></button>
|
||
<button class="tag-filter" data-tag="speed">⚡ Speed <span class="tag-count">(3)</span></button>
|
||
<button class="tag-filter" data-tag="logic">🧩 Logic <span class="tag-count">(1)</span></button>
|
||
<button class="tag-filter" data-tag="patterns">👀 Patterns <span class="tag-count">(1)</span></button>
|
||
<button class="tag-filter" data-tag="arithmetic">🏃♂️ Arithmetic <span class="tag-count">(1)</span></button>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="challenges-grid">
|
||
<button id="open-quiz-modal" class="challenge-card quiz-card" data-tags="memory,speed">
|
||
<div class="challenge-icon">⚡</div>
|
||
<div class="challenge-content">
|
||
<h4>Speed Memory Quiz</h4>
|
||
<p>Quick card displays test your reading skills</p>
|
||
<div class="challenge-tags">
|
||
<span class="tag">🔍 Memory</span>
|
||
<span class="tag">⚡ Speed</span>
|
||
</div>
|
||
</div>
|
||
</button>
|
||
|
||
<button id="open-complement-modal" class="challenge-card complement-card" data-tags="speed,arithmetic">
|
||
<div class="challenge-icon">🔥</div>
|
||
<div class="challenge-content">
|
||
<h4>Speed Complement Race</h4>
|
||
<p>Lightning-fast complement recall training</p>
|
||
<div class="challenge-tags">
|
||
<span class="tag">🏃♂️ Arithmetic</span>
|
||
<span class="tag">⚡ Speed</span>
|
||
</div>
|
||
</div>
|
||
</button>
|
||
|
||
<button id="open-sorting-modal" class="challenge-card sorting-card" data-tags="logic,patterns">
|
||
<div class="challenge-icon">🔢</div>
|
||
<div class="challenge-content">
|
||
<h4>Card Sorting Challenge</h4>
|
||
<p>Arrange cards using only abacus patterns</p>
|
||
<div class="challenge-tags">
|
||
<span class="tag">🧩 Logic</span>
|
||
<span class="tag">👀 Patterns</span>
|
||
</div>
|
||
</div>
|
||
</button>
|
||
|
||
<button id="open-matching-modal" class="challenge-card matching-card" data-tags="memory,speed">
|
||
<div class="challenge-icon">🧩</div>
|
||
<div class="challenge-content">
|
||
<h4>Matching Pairs</h4>
|
||
<p>Match abacus patterns with their numerals</p>
|
||
<div class="challenge-tags">
|
||
<span class="tag">🔍 Memory</span>
|
||
<span class="tag">⚡ Speed</span>
|
||
</div>
|
||
</div>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Quiz Modal -->
|
||
<div id="quiz-modal" class="modal">
|
||
<div class="modal-content">
|
||
<div class="modal-header">
|
||
<h2>Speed Memory Quiz</h2>
|
||
<div class="modal-controls">
|
||
<button id="quiz-fullscreen-btn" class="fullscreen-btn" title="Toggle Fullscreen">⛶</button>
|
||
<button id="close-quiz-modal" class="close-btn">×</button>
|
||
</div>
|
||
</div>
|
||
<div class="modal-body">
|
||
<p>Test your soroban reading skills! Cards will be shown briefly, then you'll enter the numbers you remember.</p>
|
||
|
||
<div class="quiz-controls">
|
||
<div class="control-group">
|
||
<label for="quiz-count">Cards to Quiz:</label>
|
||
<div class="count-buttons">
|
||
<button type="button" class="count-btn" data-count="2">2</button>
|
||
<button type="button" class="count-btn active" data-count="5">5</button>
|
||
<button type="button" class="count-btn" data-count="8">8</button>
|
||
<button type="button" class="count-btn" data-count="12">12</button>
|
||
<button type="button" class="count-btn" data-count="15">15</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="control-group">
|
||
<label for="display-time">Display Time per Card:</label>
|
||
<div class="slider-container">
|
||
<input type="range" id="display-time" min="0.5" max="10" step="0.5" value="2" />
|
||
<span class="slider-value">2.0s</span>
|
||
</div>
|
||
</div>
|
||
|
||
<button id="start-quiz" class="quiz-start-btn">Start Quiz</button>
|
||
</div>
|
||
|
||
<!-- Quiz Game Area (hidden initially) -->
|
||
<div id="quiz-game" class="quiz-game" style="display: none;">
|
||
<div class="quiz-header">
|
||
<div class="quiz-progress">
|
||
<div class="progress-bar">
|
||
<div class="progress-fill"></div>
|
||
</div>
|
||
<span class="progress-text">Card <span id="current-card">1</span> of <span id="total-cards">10</span></span>
|
||
</div>
|
||
<button id="end-quiz" class="end-game-btn">End Quiz</button>
|
||
</div>
|
||
|
||
<div class="quiz-display">
|
||
<div id="quiz-card" class="quiz-flashcard">
|
||
<!-- Card content will be inserted here -->
|
||
</div>
|
||
<div id="quiz-countdown" class="countdown">Get Ready...</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Quiz Input Phase (hidden initially) -->
|
||
<div id="quiz-input" class="quiz-input" style="display: none;">
|
||
<h3>Enter the Numbers You Remember</h3>
|
||
<div class="quiz-stats">
|
||
<div class="stats-item">
|
||
<span class="stats-label">Cards shown:</span>
|
||
<span id="cards-shown-count">0</span>
|
||
</div>
|
||
<div class="stats-item">
|
||
<span class="stats-label">Guesses left:</span>
|
||
<span id="guesses-remaining">0</span>
|
||
</div>
|
||
<div class="stats-item">
|
||
<span class="stats-label">Found:</span>
|
||
<span id="numbers-found">0</span>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="smart-input-container">
|
||
<div class="smart-input-prompt">Type the numbers you remember:</div>
|
||
<div class="number-display" id="number-display">
|
||
<span class="current-typing" id="current-typing"></span>
|
||
</div>
|
||
<input type="text" id="smart-input" style="position: absolute; left: -9999px; opacity: 0; pointer-events: none;" autocomplete="off">
|
||
</div>
|
||
|
||
<div class="found-numbers" id="found-numbers">
|
||
<!-- Accepted numbers will appear here -->
|
||
</div>
|
||
|
||
<div class="quiz-finish-buttons">
|
||
<button id="finish-quiz" class="finish-btn" style="display: none;">Finish Quiz</button>
|
||
<button id="give-up-quiz" class="give-up-btn" style="display: none;">Can't Remember More</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Quiz Results (hidden initially) -->
|
||
<div id="quiz-results" class="quiz-results" style="display: none;">
|
||
<h3>Quiz Results</h3>
|
||
<div class="score-display">
|
||
<div class="score-circle">
|
||
<span id="score-percentage">0%</span>
|
||
</div>
|
||
<div class="score-details">
|
||
<p><strong>Score:</strong> <span id="score-correct">0</span> / <span id="score-total">0</span> correct</p>
|
||
<p><strong>Time per card:</strong> <span id="result-timing">2.0s</span></p>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="results-breakdown">
|
||
<h4>Detailed Results:</h4>
|
||
<div id="results-list">
|
||
<!-- Results will be inserted here -->
|
||
</div>
|
||
</div>
|
||
|
||
<div class="quiz-actions">
|
||
<button id="retry-quiz">Try Again</button>
|
||
<button id="back-to-cards">Back to Cards</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Sorting Modal -->
|
||
<div id="sorting-modal" class="modal">
|
||
<div class="modal-content">
|
||
<div class="modal-header">
|
||
<h2>Card Sorting Challenge</h2>
|
||
<div class="modal-controls">
|
||
<button id="sorting-fullscreen-btn" class="fullscreen-btn" title="Toggle Fullscreen">⛶</button>
|
||
<button id="close-sorting-modal" class="close-btn">×</button>
|
||
</div>
|
||
</div>
|
||
<div class="modal-body">
|
||
<p>Click cards and positions to arrange them in ascending order (smallest to largest). No numerals shown - rely on reading the abacus!</p>
|
||
|
||
<div class="sorting-controls">
|
||
<div class="control-group">
|
||
<label for="sort-count">Cards to Sort:</label>
|
||
<div class="count-buttons">
|
||
<button type="button" class="sort-count-btn active" data-count="5">5</button>
|
||
<button type="button" class="sort-count-btn" data-count="8">8</button>
|
||
<button type="button" class="sort-count-btn" data-count="12">12</button>
|
||
<button type="button" class="sort-count-btn" data-count="15">15</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="sorting-actions">
|
||
<button id="start-sorting" class="sort-start-btn">Start Sorting Challenge</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Game Action Buttons (now moved to sticky header) -->
|
||
<div class="sorting-game-actions" style="display: none;">
|
||
<!-- Buttons moved to sticky header -->
|
||
</div>
|
||
|
||
<!-- Sorting Game Header (Sticky) -->
|
||
<div id="sorting-header" class="sorting-header" style="display: none;">
|
||
<div class="sorting-header-content">
|
||
<div class="sorting-status-group">
|
||
<span id="sorting-status">Ready to start</span>
|
||
<div class="sorting-timer" id="sorting-timer">0:00</div>
|
||
</div>
|
||
<div class="sorting-controls">
|
||
<button id="check-sorting" class="header-btn check-btn">Check Solution</button>
|
||
<button id="reveal-numbers" class="header-btn reveal-btn">Show Numbers</button>
|
||
<button id="new-sorting" class="header-btn new-btn">New Challenge</button>
|
||
<button id="end-sorting" class="header-btn end-btn">End Game</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Sorting Game Area -->
|
||
<div id="sorting-game" style="display: none;">
|
||
<div class="sorting-instructions">
|
||
<p><strong>Instructions:</strong> Click a card → Click position or + button to place. Click placed cards to move back.</p>
|
||
</div>
|
||
|
||
<div class="sorting-game-layout">
|
||
<div class="available-cards-section">
|
||
<h4>Available Cards</h4>
|
||
<div id="sorting-area" class="sorting-area">
|
||
<!-- Available cards will be shown here -->
|
||
</div>
|
||
</div>
|
||
|
||
<div class="sorting-slots-section">
|
||
<h4>Sorting Positions (Smallest → Largest)</h4>
|
||
<div id="position-slots" class="position-slots">
|
||
<!-- Position slots will be created here -->
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="sorting-feedback" id="sorting-feedback" style="display: none;">
|
||
<!-- Feedback will be shown here -->
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Score Report Modal -->
|
||
<div id="score-modal" class="score-modal" style="display: none;">
|
||
<div class="score-modal-content">
|
||
<div class="score-modal-header">
|
||
<h3>🎯 Sorting Challenge Results</h3>
|
||
<button class="score-modal-close">×</button>
|
||
</div>
|
||
<div id="score-modal-body" class="score-modal-body">
|
||
<!-- Score content will be inserted here -->
|
||
</div>
|
||
<div class="score-modal-footer">
|
||
<button id="score-modal-new-game" class="modal-btn new-game-btn">New Challenge</button>
|
||
<button id="score-modal-close-btn" class="modal-btn close-btn">Close</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Matching Pairs Modal -->
|
||
<div id="matching-modal" class="modal">
|
||
<div class="modal-content">
|
||
<div class="modal-header">
|
||
<h2>Matching Pairs Challenge</h2>
|
||
<div class="modal-controls">
|
||
<button id="matching-fullscreen-btn" class="fullscreen-btn" title="Toggle Fullscreen">⛶</button>
|
||
<button id="close-matching-modal" class="close-btn">×</button>
|
||
</div>
|
||
</div>
|
||
<div class="modal-body">
|
||
<p id="matching-description">Match abacus patterns with their corresponding numerals. Find all pairs in minimum moves!</p>
|
||
|
||
<div class="matching-controls">
|
||
<div class="control-group">
|
||
<label>Number of Players:</label>
|
||
<div class="mode-buttons">
|
||
<button type="button" class="mode-btn active" data-mode="single">
|
||
<div class="mode-icon">👤</div>
|
||
<div class="mode-text">Single Player</div>
|
||
</button>
|
||
<button type="button" class="mode-btn" data-mode="two-player">
|
||
<div class="mode-icon">👥</div>
|
||
<div class="mode-text">Two Player</div>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="control-group">
|
||
<label>Game Type:</label>
|
||
<div class="mode-buttons">
|
||
<button type="button" class="game-type-btn active" data-game-type="abacus-numeral">
|
||
<div class="mode-icon">🧮</div>
|
||
<div class="mode-text">Abacus / Numeral Pairs</div>
|
||
</button>
|
||
<button type="button" class="game-type-btn" data-game-type="complement-pairs">
|
||
<div class="mode-icon">🎯</div>
|
||
<div class="mode-text">Friends of 5 & 10</div>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="control-group" id="timer-controls" style="display: none;">
|
||
<label>Turn Timer:</label>
|
||
<div class="timer-buttons">
|
||
<button type="button" class="timer-btn active" data-timer="0">No Timer</button>
|
||
<button type="button" class="timer-btn" data-timer="15">15 sec</button>
|
||
<button type="button" class="timer-btn" data-timer="30">30 sec</button>
|
||
<button type="button" class="timer-btn" data-timer="60">60 sec</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="control-group">
|
||
<label for="match-grid-size">Choose Grid Size to Start:</label>
|
||
<div class="count-buttons">
|
||
<button type="button" class="match-size-btn start-game-btn" data-pairs="6">
|
||
<div class="btn-main">3×4 Grid</div>
|
||
<div class="btn-sub">6 pairs</div>
|
||
</button>
|
||
<button type="button" class="match-size-btn start-game-btn" data-pairs="8">
|
||
<div class="btn-main">4×4 Grid</div>
|
||
<div class="btn-sub">8 pairs</div>
|
||
</button>
|
||
<button type="button" class="match-size-btn start-game-btn" data-pairs="12">
|
||
<div class="btn-main">4×6 Grid</div>
|
||
<div class="btn-sub">12 pairs</div>
|
||
</button>
|
||
<button type="button" class="match-size-btn start-game-btn" data-pairs="15">
|
||
<div class="btn-main">5×6 Grid</div>
|
||
<div class="btn-sub">15 pairs</div>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Matching Game Header (Sticky) -->
|
||
<div id="matching-header" class="matching-header" style="display: none;">
|
||
<div class="matching-header-content">
|
||
<div class="matching-status-group">
|
||
<span id="matching-status">Ready to start</span>
|
||
<div class="matching-stats">
|
||
<span id="moves-counter">Moves: 0</span>
|
||
<span id="pairs-found">Pairs: 0/0</span>
|
||
<div class="matching-timer" id="matching-timer">0:00</div>
|
||
</div>
|
||
<!-- Two-player specific stats -->
|
||
<div class="player-stats" id="player-stats" style="display: none;">
|
||
<div class="player-info player1" id="player1-info">
|
||
<span class="player-name">🔵 Player 1</span>
|
||
<span class="player-score">0 pairs</span>
|
||
</div>
|
||
<div class="current-turn" id="current-turn">🔵 Player 1's Turn</div>
|
||
<div class="turn-timer" id="turn-timer" style="display: none;">30s</div>
|
||
<div class="player-info player2" id="player2-info">
|
||
<span class="player-name">🟠 Player 2</span>
|
||
<span class="player-score">0 pairs</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="matching-controls">
|
||
<button id="new-matching" class="header-btn new-btn">New Game</button>
|
||
<button id="end-matching" class="header-btn end-btn">End Game</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Matching Game Area -->
|
||
<div id="matching-game" style="display: none;">
|
||
<div class="matching-instructions">
|
||
<p><strong>Instructions:</strong> Click two cards to flip them. Purple cards (🧮) contain abacus patterns, green cards (🔢) contain numbers. Match abacus patterns with their corresponding numbers. Complete all pairs with minimum moves!</p>
|
||
</div>
|
||
|
||
<div id="matching-grid" class="matching-grid">
|
||
<!-- Matching cards will be generated here -->
|
||
</div>
|
||
|
||
<div class="matching-feedback" id="matching-feedback" style="display: none;">
|
||
<!-- Feedback will be shown here -->
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Matching Score Modal -->
|
||
<div id="matching-score-modal" class="score-modal" style="display: none;">
|
||
<div class="score-modal-content">
|
||
<div class="score-modal-header">
|
||
<h3>🧩 Matching Pairs Results</h3>
|
||
<button class="matching-score-modal-close">×</button>
|
||
</div>
|
||
<div id="matching-score-modal-body" class="score-modal-body">
|
||
<!-- Score content will be inserted here -->
|
||
</div>
|
||
<div class="score-modal-footer">
|
||
<button id="matching-score-modal-new-game" class="modal-btn new-game-btn">New Challenge</button>
|
||
<button id="matching-score-modal-close-btn" class="modal-btn close-btn">Close</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Speed Complement Race Modal -->
|
||
<div id="complement-modal" class="modal">
|
||
<div class="modal-content">
|
||
<div class="modal-header">
|
||
<h2>Speed Complement Race</h2>
|
||
<div class="modal-controls">
|
||
<button id="complement-fullscreen-btn" class="fullscreen-btn" title="Toggle Fullscreen">⛶</button>
|
||
<button id="close-complement-modal" class="close-btn">×</button>
|
||
</div>
|
||
</div>
|
||
<div class="modal-body">
|
||
<div class="complement-intro">
|
||
<div class="intro-icon">🚀</div>
|
||
<h3>Lightning-Fast Mental Math Training!</h3>
|
||
<p class="intro-description">Master complement pairs through rapid-fire practice. See a number, type its friend instantly!</p>
|
||
<div class="example-demo">
|
||
<div class="demo-equation">
|
||
<span class="demo-number">3</span>
|
||
<span class="demo-plus">+</span>
|
||
<span class="demo-answer">?</span>
|
||
<span class="demo-equals">=</span>
|
||
<span class="demo-target">5</span>
|
||
</div>
|
||
<div class="demo-instruction">👆 Type <strong>2</strong> as fast as you can!</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="complement-controls">
|
||
<div class="control-group">
|
||
<label>Training Mode:</label>
|
||
<div class="mode-buttons">
|
||
<button type="button" class="mode-btn active" data-mode="friends5">
|
||
<div class="mode-icon">✋</div>
|
||
<div class="mode-text">Friends of 5</div>
|
||
</button>
|
||
<button type="button" class="mode-btn" data-mode="friends10">
|
||
<div class="mode-icon">🔟</div>
|
||
<div class="mode-text">Friends of 10</div>
|
||
</button>
|
||
<button type="button" class="mode-btn" data-mode="mixed">
|
||
<div class="mode-icon">🎯</div>
|
||
<div class="mode-text">Mixed Challenge</div>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="control-group">
|
||
<label>Time Per Question:</label>
|
||
<div class="mode-buttons">
|
||
<button type="button" class="timeout-btn" data-timeout="preschool">
|
||
<div class="mode-icon">🧸</div>
|
||
<div class="mode-text">Preschool (20s)</div>
|
||
</button>
|
||
<button type="button" class="timeout-btn" data-timeout="kindergarten">
|
||
<div class="mode-icon">🌸</div>
|
||
<div class="mode-text">Kindergarten (15s)</div>
|
||
</button>
|
||
<button type="button" class="timeout-btn" data-timeout="relaxed">
|
||
<div class="mode-icon">🐢</div>
|
||
<div class="mode-text">Relaxed (12s)</div>
|
||
</button>
|
||
<button type="button" class="timeout-btn" data-timeout="slow">
|
||
<div class="mode-icon">🕰️</div>
|
||
<div class="mode-text">Slow (8s)</div>
|
||
</button>
|
||
<button type="button" class="timeout-btn active" data-timeout="normal">
|
||
<div class="mode-icon">⚡</div>
|
||
<div class="mode-text">Normal (5s)</div>
|
||
</button>
|
||
<button type="button" class="timeout-btn" data-timeout="fast">
|
||
<div class="mode-icon">🚀</div>
|
||
<div class="mode-text">Fast (3s)</div>
|
||
</button>
|
||
<button type="button" class="timeout-btn" data-timeout="expert">
|
||
<div class="mode-icon">⚡</div>
|
||
<div class="mode-text">Expert (2s)</div>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="control-group">
|
||
<label>Choose Your Challenge:</label>
|
||
<div class="style-buttons">
|
||
<button type="button" class="game-style-btn active" data-style="practice">
|
||
<div class="style-icon">🎯</div>
|
||
<div class="style-content">
|
||
<div class="style-title">Endurance Race</div>
|
||
<div class="style-description">Race to 20 correct answers - has finish line!</div>
|
||
</div>
|
||
</button>
|
||
<button type="button" class="game-style-btn" data-style="sprint">
|
||
<div class="style-icon">⚡</div>
|
||
<div class="style-content">
|
||
<div class="style-title">Lightning Sprint</div>
|
||
<div class="style-description">60-second time attack! Beat the clock, not the finish line!</div>
|
||
</div>
|
||
</button>
|
||
<button type="button" class="game-style-btn" data-style="survival">
|
||
<div class="style-icon">🔥</div>
|
||
<div class="style-content">
|
||
<div class="style-title">Survival Mode</div>
|
||
<div class="style-description">Keep going forever - no finish line, just survive!</div>
|
||
</div>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Game Area (hidden initially) -->
|
||
<div id="complement-game" class="complement-game" style="display: none;">
|
||
|
||
<!-- Game Header (sticky when playing) -->
|
||
<div id="complement-header" class="game-sticky-header" style="display: none;">
|
||
<div class="header-stats">
|
||
<div class="stat-item">
|
||
<span class="stat-label">Score:</span>
|
||
<span id="complement-score">0</span>
|
||
</div>
|
||
<div class="stat-item">
|
||
<span class="stat-label">Streak:</span>
|
||
<span id="complement-streak">0</span>
|
||
</div>
|
||
<div class="stat-item">
|
||
<span class="stat-label">Time:</span>
|
||
<span id="complement-time">0s</span>
|
||
</div>
|
||
</div>
|
||
<div class="header-actions">
|
||
<button id="pause-complement" class="header-btn">⏸️ Pause</button>
|
||
<button id="end-complement" class="header-btn">🏁 End</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Main Game Display -->
|
||
<div class="complement-display">
|
||
<div class="challenge-prompt">
|
||
<div class="equation-visual">
|
||
<div class="challenge-number-container">
|
||
<div class="challenge-number" id="challenge-number">7</div>
|
||
<div class="number-label">Given</div>
|
||
</div>
|
||
<div class="equation-symbol">+</div>
|
||
<div class="answer-container">
|
||
<div id="answer-display" class="answer-display">?</div>
|
||
<div class="answer-label">Your answer</div>
|
||
</div>
|
||
<div class="equation-symbol">=</div>
|
||
<div class="target-container">
|
||
<div class="target-number" id="target-number">10</div>
|
||
<div class="target-label">Target</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="timer-section">
|
||
<div class="timer-label">⏱️ Time Remaining</div>
|
||
<div class="timer-bar">
|
||
<div class="timer-fill" id="timer-fill"></div>
|
||
</div>
|
||
<div class="timer-text" id="timer-text">5.0s</div>
|
||
</div>
|
||
|
||
<!-- Race Track Section -->
|
||
<div class="race-track-section">
|
||
<div class="race-label">🏁 Mental Math Race</div>
|
||
<div class="race-track">
|
||
<div class="track-background">
|
||
<div class="track-line start-line"></div>
|
||
<div class="track-line quarter-line"></div>
|
||
<div class="track-line half-line"></div>
|
||
<div class="track-line three-quarter-line"></div>
|
||
<div class="track-line finish-line" id="finish-line"></div>
|
||
</div>
|
||
|
||
<!-- Player Racer -->
|
||
<div class="racer player-racer" id="player-racer">
|
||
<div class="racer-character">🏃♀️</div>
|
||
<div class="racer-label">You</div>
|
||
<div class="racer-progress" id="player-progress">0</div>
|
||
</div>
|
||
|
||
<!-- AI Opponents -->
|
||
<div class="racer ai-racer" id="ai-racer-1">
|
||
<div class="speech-bubble" id="speech-bubble-1">
|
||
<div class="bubble-content"></div>
|
||
<div class="bubble-tail"></div>
|
||
</div>
|
||
<div class="racer-character">🏃♂️</div>
|
||
<div class="racer-label">Swift AI</div>
|
||
</div>
|
||
|
||
<div class="racer ai-racer" id="ai-racer-2">
|
||
<div class="speech-bubble" id="speech-bubble-2">
|
||
<div class="bubble-content"></div>
|
||
<div class="bubble-tail"></div>
|
||
</div>
|
||
<div class="racer-character">🏃</div>
|
||
<div class="racer-label">Math Bot</div>
|
||
</div>
|
||
|
||
<!-- Finish Line -->
|
||
<div class="finish-zone" id="finish-zone">
|
||
<div class="finish-flag">🏁</div>
|
||
<div class="finish-text">FINISH</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Steam Train Journey (for Sprint Mode) -->
|
||
<div class="route-map" style="display: none;" id="steam-journey">
|
||
<div class="steam-journey-header">
|
||
🚂 STEAM TRAIN JOURNEY 🚂
|
||
<div class="route-progress" id="route-progress">Route 1 - Mountain Valley Express</div>
|
||
</div>
|
||
<div class="time-display" id="time-of-day">Dawn - 5:30 AM</div>
|
||
|
||
<!-- SVG Route Path -->
|
||
<div class="route-path">
|
||
<svg viewBox="0 0 800 600" width="800" height="600">
|
||
<!-- Dynamic track elements will be generated here -->
|
||
</svg>
|
||
|
||
<!-- Train locomotive -->
|
||
<div class="train-locomotive" id="train-position">
|
||
<div class="steam-effect"></div>
|
||
<div class="steam-effect" style="animation-delay: 0.3s;"></div>
|
||
<div class="steam-effect" style="animation-delay: 0.6s;"></div>
|
||
</div>
|
||
|
||
|
||
<!-- Coal shoveler -->
|
||
<div class="coal-shoveler" id="coal-worker">👷</div>
|
||
|
||
<!-- City stations -->
|
||
<div class="train-station" style="left: 50px; top: 300px;" data-city="Depot">🏭</div>
|
||
<div class="train-station" style="left: 200px; top: 200px;" data-city="Hillview">🏔️</div>
|
||
<div class="train-station" style="left: 300px; top: 250px;" data-city="Riverside">🌊</div>
|
||
<div class="train-station" style="left: 500px; top: 200px;" data-city="Summit">⛰️</div>
|
||
<div class="train-station" style="left: 700px; top: 150px;" data-city="Goldfield">⚱️</div>
|
||
<div class="train-station" style="left: 750px; top: 300px;" data-city="Capital">🏛️</div>
|
||
|
||
<!-- Geographic landmarks -->
|
||
<div class="landmark mountain" style="left: 150px; top: 180px;">⛰️</div>
|
||
<div class="landmark tree" style="left: 250px; top: 270px;">🌲</div>
|
||
<div class="landmark bridge" style="left: 350px; top: 280px;">🌉</div>
|
||
<div class="landmark mountain" style="left: 550px; top: 120px;">🏔️</div>
|
||
<div class="landmark tree" style="left: 680px; top: 170px;">🌳</div>
|
||
</div>
|
||
|
||
<!-- Steam Pressure Instrument Panel -->
|
||
<div class="instrument-panel">
|
||
<div class="panel-frame">
|
||
<div class="panel-background"></div>
|
||
<div class="pressure-gauge-container">
|
||
<svg class="pressure-gauge" viewBox="0 0 200 120" width="160" height="96">
|
||
<!-- Gauge background arc -->
|
||
<path class="gauge-background"
|
||
d="M 20 100 A 80 80 0 0 1 180 100"
|
||
fill="none"
|
||
stroke="rgba(255, 255, 255, 0.3)"
|
||
stroke-width="12"/>
|
||
|
||
<!-- Gauge progress arc -->
|
||
<path class="gauge-progress"
|
||
id="pressure-arc"
|
||
d="M 20 100 A 80 80 0 0 1 180 100"
|
||
fill="none"
|
||
stroke="#ff6b6b"
|
||
stroke-width="10"
|
||
stroke-dasharray="251.2"
|
||
stroke-dashoffset="251.2"/>
|
||
|
||
<!-- Gauge tick marks -->
|
||
<g class="gauge-ticks">
|
||
<line x1="100" y1="20" x2="100" y2="30" stroke="rgba(255,255,255,0.6)" stroke-width="2"/>
|
||
<line x1="150" y1="35" x2="145" y2="43" stroke="rgba(255,255,255,0.4)" stroke-width="1"/>
|
||
<line x1="180" y1="100" x2="170" y2="100" stroke="rgba(255,255,255,0.6)" stroke-width="2"/>
|
||
<line x1="150" y1="165" x2="145" y2="157" stroke="rgba(255,255,255,0.4)" stroke-width="1"/>
|
||
<line x1="50" y1="35" x2="55" y2="43" stroke="rgba(255,255,255,0.4)" stroke-width="1"/>
|
||
<line x1="20" y1="100" x2="30" y2="100" stroke="rgba(255,255,255,0.6)" stroke-width="2"/>
|
||
<line x1="50" y1="165" x2="55" y2="157" stroke="rgba(255,255,255,0.4)" stroke-width="1"/>
|
||
</g>
|
||
|
||
<!-- Pressure indicator needle (narrow isosceles triangle) -->
|
||
<g class="pressure-needle" id="pressure-needle" transform="rotate(0 100 100)">
|
||
<polygon points="100,95 98,100 102,100"
|
||
fill="#ffd700"
|
||
stroke="#ffed4e"
|
||
stroke-width="1"/>
|
||
<circle cx="100" cy="100" r="6" fill="rgba(255,255,255,0.9)" stroke="rgba(255,255,255,0.6)" stroke-width="2"/>
|
||
</g>
|
||
|
||
<!-- Gauge labels -->
|
||
<text x="30" y="105" fill="rgba(255,255,255,0.7)" font-family="sans-serif" font-size="10" font-weight="600">0</text>
|
||
<text x="95" y="25" fill="rgba(255,255,255,0.7)" font-family="sans-serif" font-size="10" font-weight="600">50</text>
|
||
<text x="175" y="105" fill="rgba(255,255,255,0.7)" font-family="sans-serif" font-size="10" font-weight="600">100</text>
|
||
</svg>
|
||
<div class="gauge-title">🚂 Steam Power</div>
|
||
<div class="gauge-value" id="pressure-value">0 PSI</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Passenger car -->
|
||
<div class="passenger-car" id="passenger-display">
|
||
<h4>👥 Passengers</h4>
|
||
<div class="passenger-list" id="passenger-list">
|
||
<!-- Passengers will be added dynamically -->
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="race-stats">
|
||
<div class="race-stat">
|
||
<span class="stat-label">Correct:</span>
|
||
<span id="race-correct">0</span>
|
||
</div>
|
||
<div class="race-stat">
|
||
<span class="stat-label">Goal:</span>
|
||
<span id="race-goal">20</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
|
||
<!-- Feedback Area -->
|
||
<div class="complement-feedback-area">
|
||
<div class="input-feedback" id="input-feedback"></div>
|
||
<div class="adaptive-feedback" id="adaptive-feedback"></div>
|
||
</div>
|
||
|
||
<!-- Visual Complement Display -->
|
||
<div class="visual-complement" id="visual-complement" style="display: none;">
|
||
<div class="complement-equation">
|
||
<span class="number-part" id="number-part">7</span>
|
||
<span class="plus">+</span>
|
||
<span class="complement-part" id="complement-part">3</span>
|
||
<span class="equals">=</span>
|
||
<span class="sum-part" id="sum-part">10</span>
|
||
</div>
|
||
<div class="abacus-visualization" id="abacus-visualization">
|
||
<!-- Mini abacus showing the complement will go here -->
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Progress & Achievement Display -->
|
||
<div class="progress-display">
|
||
<div class="progress-bar">
|
||
<div class="progress-fill" id="progress-fill"></div>
|
||
<span class="progress-text" id="progress-text">0/10 Complete</span>
|
||
</div>
|
||
<div class="achievement-notice" id="achievement-notice" style="display: none;">
|
||
<!-- Achievement unlocks will appear here -->
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Complement Score Modal -->
|
||
<div id="complement-score-modal" class="score-modal" style="display: none;">
|
||
<div class="score-modal-content">
|
||
<div class="score-modal-header">
|
||
<h3>🔥 Complement Race Results</h3>
|
||
<button class="complement-score-modal-close">×</button>
|
||
</div>
|
||
<div id="complement-score-modal-body" class="score-modal-body">
|
||
<!-- Score content will be inserted here -->
|
||
</div>
|
||
<div class="score-modal-footer">
|
||
<button id="complement-score-modal-new-game" class="modal-btn new-game-btn">Race Again</button>
|
||
<button id="complement-score-modal-close-btn" class="modal-btn close-btn">Close</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
|
||
<!-- Section 4: Flashcards -->
|
||
<section id="flashcards" class="page-section">
|
||
<div class="section-header">
|
||
<h2 class="section-title">🧮 Practice Flashcards</h2>
|
||
<p class="section-subtitle">Interactive cards for hands-on learning</p>
|
||
</div>
|
||
|
||
<div class="cards-grid" id="cards-grid">
|
||
|
||
<div class="flashcard" data-number="1">
|
||
<div class="card-number">#1</div>
|
||
<div class="abacus-container">
|
||
<svg class="typst-doc" viewBox="0 0 108 108" width="108pt" height="108pt" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:h5="http://www.w3.org/1999/xhtml">
|
||
<g>
|
||
<g transform="translate(5.400000000000001 5.400000000000001)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(36.1 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 -11.400000000000002)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(11 10)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 80 L 3 80 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(6.5 10)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 33)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 53)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 65.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 78)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(0 30)">
|
||
<path class="typst-shape" fill="#000000" fill-rule="nonzero" d="M 0 0 L 0 2 L 25 2 L 25 0 Z "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</svg>
|
||
</div>
|
||
<div class="numeral"><span style="color: #2E86AB;">1</span></div>
|
||
</div>
|
||
<div class="flashcard" data-number="2">
|
||
<div class="card-number">#2</div>
|
||
<div class="abacus-container">
|
||
<svg class="typst-doc" viewBox="0 0 108 108" width="108pt" height="108pt" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:h5="http://www.w3.org/1999/xhtml">
|
||
<g>
|
||
<g transform="translate(5.400000000000001 5.400000000000001)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(36.1 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 -11.400000000000002)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(11 10)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 80 L 3 80 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(6.5 10)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 33)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 45.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 65.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 78)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(0 30)">
|
||
<path class="typst-shape" fill="#000000" fill-rule="nonzero" d="M 0 0 L 0 2 L 25 2 L 25 0 Z "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</svg>
|
||
</div>
|
||
<div class="numeral"><span style="color: #2E86AB;">2</span></div>
|
||
</div>
|
||
<div class="flashcard" data-number="3">
|
||
<div class="card-number">#3</div>
|
||
<div class="abacus-container">
|
||
<svg class="typst-doc" viewBox="0 0 108 108" width="108pt" height="108pt" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:h5="http://www.w3.org/1999/xhtml">
|
||
<g>
|
||
<g transform="translate(5.400000000000001 5.400000000000001)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(36.1 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 -11.400000000000002)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(11 10)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 80 L 3 80 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(6.5 10)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 33)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 45.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 58)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 78)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(0 30)">
|
||
<path class="typst-shape" fill="#000000" fill-rule="nonzero" d="M 0 0 L 0 2 L 25 2 L 25 0 Z "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</svg>
|
||
</div>
|
||
<div class="numeral"><span style="color: #2E86AB;">3</span></div>
|
||
</div>
|
||
<div class="flashcard" data-number="4">
|
||
<div class="card-number">#4</div>
|
||
<div class="abacus-container">
|
||
<svg class="typst-doc" viewBox="0 0 108 108" width="108pt" height="108pt" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:h5="http://www.w3.org/1999/xhtml">
|
||
<g>
|
||
<g transform="translate(5.400000000000001 5.400000000000001)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(36.1 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 -11.400000000000002)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(11 10)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 72.5 L 3 72.5 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(6.5 10)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 33)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 45.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 58)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 70.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(0 30)">
|
||
<path class="typst-shape" fill="#000000" fill-rule="nonzero" d="M 0 0 L 0 2 L 25 2 L 25 0 Z "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</svg>
|
||
</div>
|
||
<div class="numeral"><span style="color: #2E86AB;">4</span></div>
|
||
</div>
|
||
<div class="flashcard" data-number="5">
|
||
<div class="card-number">#5</div>
|
||
<div class="abacus-container">
|
||
<svg class="typst-doc" viewBox="0 0 108 108" width="108pt" height="108pt" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:h5="http://www.w3.org/1999/xhtml">
|
||
<g>
|
||
<g transform="translate(5.400000000000001 5.400000000000001)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(36.1 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 -11.400000000000002)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(11 17)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 72.5 L 3 72.5 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(6.5 17)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 40)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 52.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 65)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 77.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(0 30)">
|
||
<path class="typst-shape" fill="#000000" fill-rule="nonzero" d="M 0 0 L 0 2 L 25 2 L 25 0 Z "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</svg>
|
||
</div>
|
||
<div class="numeral"><span style="color: #2E86AB;">5</span></div>
|
||
</div>
|
||
<div class="flashcard" data-number="6">
|
||
<div class="card-number">#6</div>
|
||
<div class="abacus-container">
|
||
<svg class="typst-doc" viewBox="0 0 108 108" width="108pt" height="108pt" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:h5="http://www.w3.org/1999/xhtml">
|
||
<g>
|
||
<g transform="translate(5.400000000000001 5.400000000000001)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(36.1 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 -11.400000000000002)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(11 17)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 73 L 3 73 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(6.5 17)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 33)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 53)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 65.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 78)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(0 30)">
|
||
<path class="typst-shape" fill="#000000" fill-rule="nonzero" d="M 0 0 L 0 2 L 25 2 L 25 0 Z "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</svg>
|
||
</div>
|
||
<div class="numeral"><span style="color: #2E86AB;">6</span></div>
|
||
</div>
|
||
<div class="flashcard" data-number="7">
|
||
<div class="card-number">#7</div>
|
||
<div class="abacus-container">
|
||
<svg class="typst-doc" viewBox="0 0 108 108" width="108pt" height="108pt" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:h5="http://www.w3.org/1999/xhtml">
|
||
<g>
|
||
<g transform="translate(5.400000000000001 5.400000000000001)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(36.1 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 -11.400000000000002)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(11 17)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 73 L 3 73 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(6.5 17)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 33)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 45.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 65.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 78)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(0 30)">
|
||
<path class="typst-shape" fill="#000000" fill-rule="nonzero" d="M 0 0 L 0 2 L 25 2 L 25 0 Z "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</svg>
|
||
</div>
|
||
<div class="numeral"><span style="color: #2E86AB;">7</span></div>
|
||
</div>
|
||
<div class="flashcard" data-number="8">
|
||
<div class="card-number">#8</div>
|
||
<div class="abacus-container">
|
||
<svg class="typst-doc" viewBox="0 0 108 108" width="108pt" height="108pt" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:h5="http://www.w3.org/1999/xhtml">
|
||
<g>
|
||
<g transform="translate(5.400000000000001 5.400000000000001)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(36.1 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 -11.400000000000002)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(11 17)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 73 L 3 73 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(6.5 17)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 33)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 45.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 58)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 78)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(0 30)">
|
||
<path class="typst-shape" fill="#000000" fill-rule="nonzero" d="M 0 0 L 0 2 L 25 2 L 25 0 Z "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</svg>
|
||
</div>
|
||
<div class="numeral"><span style="color: #2E86AB;">8</span></div>
|
||
</div>
|
||
<div class="flashcard" data-number="9">
|
||
<div class="card-number">#9</div>
|
||
<div class="abacus-container">
|
||
<svg class="typst-doc" viewBox="0 0 108 108" width="108pt" height="108pt" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:h5="http://www.w3.org/1999/xhtml">
|
||
<g>
|
||
<g transform="translate(5.400000000000001 5.400000000000001)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(36.1 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 -11.400000000000002)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(11 17)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 65.5 L 3 65.5 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(6.5 17)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 33)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 45.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 58)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 70.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(0 30)">
|
||
<path class="typst-shape" fill="#000000" fill-rule="nonzero" d="M 0 0 L 0 2 L 25 2 L 25 0 Z "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</svg>
|
||
</div>
|
||
<div class="numeral"><span style="color: #2E86AB;">9</span></div>
|
||
</div>
|
||
<div class="flashcard" data-number="10">
|
||
<div class="card-number">#10</div>
|
||
<div class="abacus-container">
|
||
<svg class="typst-doc" viewBox="0 0 108 108" width="108pt" height="108pt" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:h5="http://www.w3.org/1999/xhtml">
|
||
<g>
|
||
<g transform="translate(5.400000000000001 5.400000000000001)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(23.599999999999998 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 -11.400000000000002)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(11 10)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 80 L 3 80 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(6.5 10)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 33)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 53)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 65.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 78)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(36 10)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 79.5 L 3 79.5 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(31.5 10)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 40)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 52.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 65)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 77.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(0 30)">
|
||
<path class="typst-shape" fill="#000000" fill-rule="nonzero" d="M 0 0 L 0 2 L 50 2 L 50 0 Z "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</svg>
|
||
</div>
|
||
<div class="numeral"><span style="color: #A23B72;">1</span><span style="color: #2E86AB;">0</span></div>
|
||
</div>
|
||
<div class="flashcard" data-number="11">
|
||
<div class="card-number">#11</div>
|
||
<div class="abacus-container">
|
||
<svg class="typst-doc" viewBox="0 0 108 108" width="108pt" height="108pt" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:h5="http://www.w3.org/1999/xhtml">
|
||
<g>
|
||
<g transform="translate(5.400000000000001 5.400000000000001)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(23.599999999999998 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 -11.400000000000002)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(11 10)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 80 L 3 80 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(6.5 10)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 33)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 53)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 65.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 78)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(36 10)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 80 L 3 80 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(31.5 10)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 33)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 53)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 65.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 78)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(0 30)">
|
||
<path class="typst-shape" fill="#000000" fill-rule="nonzero" d="M 0 0 L 0 2 L 50 2 L 50 0 Z "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</svg>
|
||
</div>
|
||
<div class="numeral"><span style="color: #A23B72;">1</span><span style="color: #2E86AB;">1</span></div>
|
||
</div>
|
||
<div class="flashcard" data-number="12">
|
||
<div class="card-number">#12</div>
|
||
<div class="abacus-container">
|
||
<svg class="typst-doc" viewBox="0 0 108 108" width="108pt" height="108pt" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:h5="http://www.w3.org/1999/xhtml">
|
||
<g>
|
||
<g transform="translate(5.400000000000001 5.400000000000001)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(23.599999999999998 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 -11.400000000000002)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(11 10)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 80 L 3 80 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(6.5 10)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 33)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 53)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 65.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 78)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(36 10)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 80 L 3 80 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(31.5 10)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 33)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 45.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 65.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 78)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(0 30)">
|
||
<path class="typst-shape" fill="#000000" fill-rule="nonzero" d="M 0 0 L 0 2 L 50 2 L 50 0 Z "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</svg>
|
||
</div>
|
||
<div class="numeral"><span style="color: #A23B72;">1</span><span style="color: #2E86AB;">2</span></div>
|
||
</div>
|
||
<div class="flashcard" data-number="13">
|
||
<div class="card-number">#13</div>
|
||
<div class="abacus-container">
|
||
<svg class="typst-doc" viewBox="0 0 108 108" width="108pt" height="108pt" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:h5="http://www.w3.org/1999/xhtml">
|
||
<g>
|
||
<g transform="translate(5.400000000000001 5.400000000000001)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(23.599999999999998 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 -11.400000000000002)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(11 10)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 80 L 3 80 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(6.5 10)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 33)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 53)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 65.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 78)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(36 10)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 80 L 3 80 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(31.5 10)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 33)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 45.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 58)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 78)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(0 30)">
|
||
<path class="typst-shape" fill="#000000" fill-rule="nonzero" d="M 0 0 L 0 2 L 50 2 L 50 0 Z "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</svg>
|
||
</div>
|
||
<div class="numeral"><span style="color: #A23B72;">1</span><span style="color: #2E86AB;">3</span></div>
|
||
</div>
|
||
<div class="flashcard" data-number="14">
|
||
<div class="card-number">#14</div>
|
||
<div class="abacus-container">
|
||
<svg class="typst-doc" viewBox="0 0 108 108" width="108pt" height="108pt" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:h5="http://www.w3.org/1999/xhtml">
|
||
<g>
|
||
<g transform="translate(5.400000000000001 5.400000000000001)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(23.599999999999998 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 -11.400000000000002)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(11 10)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 80 L 3 80 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(6.5 10)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 33)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 53)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 65.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 78)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(36 10)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 72.5 L 3 72.5 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(31.5 10)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 33)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 45.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 58)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 70.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(0 30)">
|
||
<path class="typst-shape" fill="#000000" fill-rule="nonzero" d="M 0 0 L 0 2 L 50 2 L 50 0 Z "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</svg>
|
||
</div>
|
||
<div class="numeral"><span style="color: #A23B72;">1</span><span style="color: #2E86AB;">4</span></div>
|
||
</div>
|
||
<div class="flashcard" data-number="15">
|
||
<div class="card-number">#15</div>
|
||
<div class="abacus-container">
|
||
<svg class="typst-doc" viewBox="0 0 108 108" width="108pt" height="108pt" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:h5="http://www.w3.org/1999/xhtml">
|
||
<g>
|
||
<g transform="translate(5.400000000000001 5.400000000000001)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(23.599999999999998 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 -11.400000000000002)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(11 10)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 80 L 3 80 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(6.5 10)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 33)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 53)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 65.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 78)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(36 17)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 72.5 L 3 72.5 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(31.5 17)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 40)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 52.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 65)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 77.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(0 30)">
|
||
<path class="typst-shape" fill="#000000" fill-rule="nonzero" d="M 0 0 L 0 2 L 50 2 L 50 0 Z "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</svg>
|
||
</div>
|
||
<div class="numeral"><span style="color: #A23B72;">1</span><span style="color: #2E86AB;">5</span></div>
|
||
</div>
|
||
<div class="flashcard" data-number="16">
|
||
<div class="card-number">#16</div>
|
||
<div class="abacus-container">
|
||
<svg class="typst-doc" viewBox="0 0 108 108" width="108pt" height="108pt" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:h5="http://www.w3.org/1999/xhtml">
|
||
<g>
|
||
<g transform="translate(5.400000000000001 5.400000000000001)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(23.599999999999998 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 -11.400000000000002)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(11 10)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 80 L 3 80 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(6.5 10)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 33)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 53)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 65.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 78)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(36 17)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 73 L 3 73 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(31.5 17)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 33)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 53)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 65.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 78)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(0 30)">
|
||
<path class="typst-shape" fill="#000000" fill-rule="nonzero" d="M 0 0 L 0 2 L 50 2 L 50 0 Z "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</svg>
|
||
</div>
|
||
<div class="numeral"><span style="color: #A23B72;">1</span><span style="color: #2E86AB;">6</span></div>
|
||
</div>
|
||
<div class="flashcard" data-number="17">
|
||
<div class="card-number">#17</div>
|
||
<div class="abacus-container">
|
||
<svg class="typst-doc" viewBox="0 0 108 108" width="108pt" height="108pt" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:h5="http://www.w3.org/1999/xhtml">
|
||
<g>
|
||
<g transform="translate(5.400000000000001 5.400000000000001)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(23.599999999999998 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 -11.400000000000002)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(11 10)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 80 L 3 80 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(6.5 10)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 33)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 53)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 65.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 78)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(36 17)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 73 L 3 73 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(31.5 17)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 33)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 45.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 65.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 78)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(0 30)">
|
||
<path class="typst-shape" fill="#000000" fill-rule="nonzero" d="M 0 0 L 0 2 L 50 2 L 50 0 Z "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</svg>
|
||
</div>
|
||
<div class="numeral"><span style="color: #A23B72;">1</span><span style="color: #2E86AB;">7</span></div>
|
||
</div>
|
||
<div class="flashcard" data-number="18">
|
||
<div class="card-number">#18</div>
|
||
<div class="abacus-container">
|
||
<svg class="typst-doc" viewBox="0 0 108 108" width="108pt" height="108pt" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:h5="http://www.w3.org/1999/xhtml">
|
||
<g>
|
||
<g transform="translate(5.400000000000001 5.400000000000001)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(23.599999999999998 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 -11.400000000000002)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(11 10)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 80 L 3 80 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(6.5 10)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 33)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 53)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 65.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 78)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(36 17)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 73 L 3 73 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(31.5 17)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 33)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 45.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 58)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 78)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(0 30)">
|
||
<path class="typst-shape" fill="#000000" fill-rule="nonzero" d="M 0 0 L 0 2 L 50 2 L 50 0 Z "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</svg>
|
||
</div>
|
||
<div class="numeral"><span style="color: #A23B72;">1</span><span style="color: #2E86AB;">8</span></div>
|
||
</div>
|
||
<div class="flashcard" data-number="19">
|
||
<div class="card-number">#19</div>
|
||
<div class="abacus-container">
|
||
<svg class="typst-doc" viewBox="0 0 108 108" width="108pt" height="108pt" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:h5="http://www.w3.org/1999/xhtml">
|
||
<g>
|
||
<g transform="translate(5.400000000000001 5.400000000000001)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(23.599999999999998 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 -11.400000000000002)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(11 10)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 80 L 3 80 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(6.5 10)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 33)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 53)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 65.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 78)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(36 17)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 65.5 L 3 65.5 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(31.5 17)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 33)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 45.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 58)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 70.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(0 30)">
|
||
<path class="typst-shape" fill="#000000" fill-rule="nonzero" d="M 0 0 L 0 2 L 50 2 L 50 0 Z "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</svg>
|
||
</div>
|
||
<div class="numeral"><span style="color: #A23B72;">1</span><span style="color: #2E86AB;">9</span></div>
|
||
</div>
|
||
<div class="flashcard" data-number="20">
|
||
<div class="card-number">#20</div>
|
||
<div class="abacus-container">
|
||
<svg class="typst-doc" viewBox="0 0 108 108" width="108pt" height="108pt" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:h5="http://www.w3.org/1999/xhtml">
|
||
<g>
|
||
<g transform="translate(5.400000000000001 5.400000000000001)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(23.599999999999998 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 -11.400000000000002)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(11 10)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 80 L 3 80 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(6.5 10)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 33)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 45.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 65.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 78)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(36 10)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 79.5 L 3 79.5 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(31.5 10)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 40)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 52.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 65)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 77.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(0 30)">
|
||
<path class="typst-shape" fill="#000000" fill-rule="nonzero" d="M 0 0 L 0 2 L 50 2 L 50 0 Z "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</svg>
|
||
</div>
|
||
<div class="numeral"><span style="color: #A23B72;">2</span><span style="color: #2E86AB;">0</span></div>
|
||
</div>
|
||
<div class="flashcard" data-number="21">
|
||
<div class="card-number">#21</div>
|
||
<div class="abacus-container">
|
||
<svg class="typst-doc" viewBox="0 0 108 108" width="108pt" height="108pt" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:h5="http://www.w3.org/1999/xhtml">
|
||
<g>
|
||
<g transform="translate(5.400000000000001 5.400000000000001)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(23.599999999999998 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 -11.400000000000002)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(11 10)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 80 L 3 80 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(6.5 10)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 33)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 45.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 65.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 78)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(36 10)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 80 L 3 80 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(31.5 10)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 33)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 53)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 65.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 78)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(0 30)">
|
||
<path class="typst-shape" fill="#000000" fill-rule="nonzero" d="M 0 0 L 0 2 L 50 2 L 50 0 Z "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</svg>
|
||
</div>
|
||
<div class="numeral"><span style="color: #A23B72;">2</span><span style="color: #2E86AB;">1</span></div>
|
||
</div>
|
||
<div class="flashcard" data-number="22">
|
||
<div class="card-number">#22</div>
|
||
<div class="abacus-container">
|
||
<svg class="typst-doc" viewBox="0 0 108 108" width="108pt" height="108pt" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:h5="http://www.w3.org/1999/xhtml">
|
||
<g>
|
||
<g transform="translate(5.400000000000001 5.400000000000001)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(23.599999999999998 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 -11.400000000000002)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(11 10)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 80 L 3 80 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(6.5 10)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 33)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 45.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 65.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 78)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(36 10)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 80 L 3 80 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(31.5 10)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 33)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 45.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 65.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 78)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(0 30)">
|
||
<path class="typst-shape" fill="#000000" fill-rule="nonzero" d="M 0 0 L 0 2 L 50 2 L 50 0 Z "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</svg>
|
||
</div>
|
||
<div class="numeral"><span style="color: #A23B72;">2</span><span style="color: #2E86AB;">2</span></div>
|
||
</div>
|
||
<div class="flashcard" data-number="23">
|
||
<div class="card-number">#23</div>
|
||
<div class="abacus-container">
|
||
<svg class="typst-doc" viewBox="0 0 108 108" width="108pt" height="108pt" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:h5="http://www.w3.org/1999/xhtml">
|
||
<g>
|
||
<g transform="translate(5.400000000000001 5.400000000000001)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(23.599999999999998 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 -11.400000000000002)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(11 10)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 80 L 3 80 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(6.5 10)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 33)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 45.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 65.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 78)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(36 10)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 80 L 3 80 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(31.5 10)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 33)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 45.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 58)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 78)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(0 30)">
|
||
<path class="typst-shape" fill="#000000" fill-rule="nonzero" d="M 0 0 L 0 2 L 50 2 L 50 0 Z "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</svg>
|
||
</div>
|
||
<div class="numeral"><span style="color: #A23B72;">2</span><span style="color: #2E86AB;">3</span></div>
|
||
</div>
|
||
<div class="flashcard" data-number="24">
|
||
<div class="card-number">#24</div>
|
||
<div class="abacus-container">
|
||
<svg class="typst-doc" viewBox="0 0 108 108" width="108pt" height="108pt" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:h5="http://www.w3.org/1999/xhtml">
|
||
<g>
|
||
<g transform="translate(5.400000000000001 5.400000000000001)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(23.599999999999998 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 -11.400000000000002)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(11 10)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 80 L 3 80 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(6.5 10)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 33)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 45.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 65.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 78)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(36 10)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 72.5 L 3 72.5 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(31.5 10)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 33)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 45.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 58)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 70.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(0 30)">
|
||
<path class="typst-shape" fill="#000000" fill-rule="nonzero" d="M 0 0 L 0 2 L 50 2 L 50 0 Z "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</svg>
|
||
</div>
|
||
<div class="numeral"><span style="color: #A23B72;">2</span><span style="color: #2E86AB;">4</span></div>
|
||
</div>
|
||
<div class="flashcard" data-number="25">
|
||
<div class="card-number">#25</div>
|
||
<div class="abacus-container">
|
||
<svg class="typst-doc" viewBox="0 0 108 108" width="108pt" height="108pt" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:h5="http://www.w3.org/1999/xhtml">
|
||
<g>
|
||
<g transform="translate(5.400000000000001 5.400000000000001)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(23.599999999999998 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 -11.400000000000002)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(11 10)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 80 L 3 80 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(6.5 10)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 33)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 45.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 65.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 78)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(36 17)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 72.5 L 3 72.5 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(31.5 17)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 40)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 52.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 65)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 77.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(0 30)">
|
||
<path class="typst-shape" fill="#000000" fill-rule="nonzero" d="M 0 0 L 0 2 L 50 2 L 50 0 Z "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</svg>
|
||
</div>
|
||
<div class="numeral"><span style="color: #A23B72;">2</span><span style="color: #2E86AB;">5</span></div>
|
||
</div>
|
||
<div class="flashcard" data-number="26">
|
||
<div class="card-number">#26</div>
|
||
<div class="abacus-container">
|
||
<svg class="typst-doc" viewBox="0 0 108 108" width="108pt" height="108pt" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:h5="http://www.w3.org/1999/xhtml">
|
||
<g>
|
||
<g transform="translate(5.400000000000001 5.400000000000001)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(23.599999999999998 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 -11.400000000000002)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(11 10)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 80 L 3 80 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(6.5 10)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 33)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 45.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 65.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 78)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(36 17)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 73 L 3 73 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(31.5 17)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 33)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 53)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 65.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 78)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(0 30)">
|
||
<path class="typst-shape" fill="#000000" fill-rule="nonzero" d="M 0 0 L 0 2 L 50 2 L 50 0 Z "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</svg>
|
||
</div>
|
||
<div class="numeral"><span style="color: #A23B72;">2</span><span style="color: #2E86AB;">6</span></div>
|
||
</div>
|
||
<div class="flashcard" data-number="27">
|
||
<div class="card-number">#27</div>
|
||
<div class="abacus-container">
|
||
<svg class="typst-doc" viewBox="0 0 108 108" width="108pt" height="108pt" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:h5="http://www.w3.org/1999/xhtml">
|
||
<g>
|
||
<g transform="translate(5.400000000000001 5.400000000000001)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(23.599999999999998 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 -11.400000000000002)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(11 10)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 80 L 3 80 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(6.5 10)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 33)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 45.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 65.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 78)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(36 17)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 73 L 3 73 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(31.5 17)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 33)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 45.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 65.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 78)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(0 30)">
|
||
<path class="typst-shape" fill="#000000" fill-rule="nonzero" d="M 0 0 L 0 2 L 50 2 L 50 0 Z "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</svg>
|
||
</div>
|
||
<div class="numeral"><span style="color: #A23B72;">2</span><span style="color: #2E86AB;">7</span></div>
|
||
</div>
|
||
<div class="flashcard" data-number="28">
|
||
<div class="card-number">#28</div>
|
||
<div class="abacus-container">
|
||
<svg class="typst-doc" viewBox="0 0 108 108" width="108pt" height="108pt" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:h5="http://www.w3.org/1999/xhtml">
|
||
<g>
|
||
<g transform="translate(5.400000000000001 5.400000000000001)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(23.599999999999998 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 -11.400000000000002)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(11 10)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 80 L 3 80 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(6.5 10)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 33)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 45.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 65.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 78)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(36 17)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 73 L 3 73 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(31.5 17)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 33)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 45.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 58)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 78)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(0 30)">
|
||
<path class="typst-shape" fill="#000000" fill-rule="nonzero" d="M 0 0 L 0 2 L 50 2 L 50 0 Z "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</svg>
|
||
</div>
|
||
<div class="numeral"><span style="color: #A23B72;">2</span><span style="color: #2E86AB;">8</span></div>
|
||
</div>
|
||
<div class="flashcard" data-number="29">
|
||
<div class="card-number">#29</div>
|
||
<div class="abacus-container">
|
||
<svg class="typst-doc" viewBox="0 0 108 108" width="108pt" height="108pt" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:h5="http://www.w3.org/1999/xhtml">
|
||
<g>
|
||
<g transform="translate(5.400000000000001 5.400000000000001)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(23.599999999999998 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 -11.400000000000002)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(11 10)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 80 L 3 80 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(6.5 10)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 33)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 45.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 65.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 78)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(36 17)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 65.5 L 3 65.5 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(31.5 17)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 33)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 45.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 58)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 70.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(0 30)">
|
||
<path class="typst-shape" fill="#000000" fill-rule="nonzero" d="M 0 0 L 0 2 L 50 2 L 50 0 Z "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</svg>
|
||
</div>
|
||
<div class="numeral"><span style="color: #A23B72;">2</span><span style="color: #2E86AB;">9</span></div>
|
||
</div>
|
||
<div class="flashcard" data-number="30">
|
||
<div class="card-number">#30</div>
|
||
<div class="abacus-container">
|
||
<svg class="typst-doc" viewBox="0 0 108 108" width="108pt" height="108pt" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:h5="http://www.w3.org/1999/xhtml">
|
||
<g>
|
||
<g transform="translate(5.400000000000001 5.400000000000001)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(23.599999999999998 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 -11.400000000000002)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(11 10)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 80 L 3 80 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(6.5 10)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 33)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 45.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 58)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 78)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(36 10)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 79.5 L 3 79.5 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(31.5 10)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 40)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 52.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 65)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 77.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(0 30)">
|
||
<path class="typst-shape" fill="#000000" fill-rule="nonzero" d="M 0 0 L 0 2 L 50 2 L 50 0 Z "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</svg>
|
||
</div>
|
||
<div class="numeral"><span style="color: #A23B72;">3</span><span style="color: #2E86AB;">0</span></div>
|
||
</div>
|
||
<div class="flashcard" data-number="31">
|
||
<div class="card-number">#31</div>
|
||
<div class="abacus-container">
|
||
<svg class="typst-doc" viewBox="0 0 108 108" width="108pt" height="108pt" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:h5="http://www.w3.org/1999/xhtml">
|
||
<g>
|
||
<g transform="translate(5.400000000000001 5.400000000000001)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(23.599999999999998 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 -11.400000000000002)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(11 10)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 80 L 3 80 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(6.5 10)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 33)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 45.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 58)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 78)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(36 10)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 80 L 3 80 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(31.5 10)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 33)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 53)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 65.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 78)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(0 30)">
|
||
<path class="typst-shape" fill="#000000" fill-rule="nonzero" d="M 0 0 L 0 2 L 50 2 L 50 0 Z "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</svg>
|
||
</div>
|
||
<div class="numeral"><span style="color: #A23B72;">3</span><span style="color: #2E86AB;">1</span></div>
|
||
</div>
|
||
<div class="flashcard" data-number="32">
|
||
<div class="card-number">#32</div>
|
||
<div class="abacus-container">
|
||
<svg class="typst-doc" viewBox="0 0 108 108" width="108pt" height="108pt" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:h5="http://www.w3.org/1999/xhtml">
|
||
<g>
|
||
<g transform="translate(5.400000000000001 5.400000000000001)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(23.599999999999998 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 -11.400000000000002)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(11 10)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 80 L 3 80 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(6.5 10)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 33)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 45.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 58)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 78)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(36 10)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 80 L 3 80 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(31.5 10)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 33)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 45.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 65.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 78)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(0 30)">
|
||
<path class="typst-shape" fill="#000000" fill-rule="nonzero" d="M 0 0 L 0 2 L 50 2 L 50 0 Z "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</svg>
|
||
</div>
|
||
<div class="numeral"><span style="color: #A23B72;">3</span><span style="color: #2E86AB;">2</span></div>
|
||
</div>
|
||
<div class="flashcard" data-number="33">
|
||
<div class="card-number">#33</div>
|
||
<div class="abacus-container">
|
||
<svg class="typst-doc" viewBox="0 0 108 108" width="108pt" height="108pt" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:h5="http://www.w3.org/1999/xhtml">
|
||
<g>
|
||
<g transform="translate(5.400000000000001 5.400000000000001)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(23.599999999999998 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 -11.400000000000002)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(11 10)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 80 L 3 80 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(6.5 10)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 33)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 45.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 58)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 78)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(36 10)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 80 L 3 80 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(31.5 10)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 33)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 45.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 58)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 78)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(0 30)">
|
||
<path class="typst-shape" fill="#000000" fill-rule="nonzero" d="M 0 0 L 0 2 L 50 2 L 50 0 Z "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</svg>
|
||
</div>
|
||
<div class="numeral"><span style="color: #A23B72;">3</span><span style="color: #2E86AB;">3</span></div>
|
||
</div>
|
||
<div class="flashcard" data-number="34">
|
||
<div class="card-number">#34</div>
|
||
<div class="abacus-container">
|
||
<svg class="typst-doc" viewBox="0 0 108 108" width="108pt" height="108pt" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:h5="http://www.w3.org/1999/xhtml">
|
||
<g>
|
||
<g transform="translate(5.400000000000001 5.400000000000001)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(23.599999999999998 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 -11.400000000000002)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(11 10)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 80 L 3 80 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(6.5 10)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 33)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 45.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 58)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 78)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(36 10)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 72.5 L 3 72.5 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(31.5 10)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 33)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 45.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 58)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 70.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(0 30)">
|
||
<path class="typst-shape" fill="#000000" fill-rule="nonzero" d="M 0 0 L 0 2 L 50 2 L 50 0 Z "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</svg>
|
||
</div>
|
||
<div class="numeral"><span style="color: #A23B72;">3</span><span style="color: #2E86AB;">4</span></div>
|
||
</div>
|
||
<div class="flashcard" data-number="35">
|
||
<div class="card-number">#35</div>
|
||
<div class="abacus-container">
|
||
<svg class="typst-doc" viewBox="0 0 108 108" width="108pt" height="108pt" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:h5="http://www.w3.org/1999/xhtml">
|
||
<g>
|
||
<g transform="translate(5.400000000000001 5.400000000000001)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(23.599999999999998 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 -11.400000000000002)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(11 10)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 80 L 3 80 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(6.5 10)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 33)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 45.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 58)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 78)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(36 17)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 72.5 L 3 72.5 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(31.5 17)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 40)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 52.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 65)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 77.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(0 30)">
|
||
<path class="typst-shape" fill="#000000" fill-rule="nonzero" d="M 0 0 L 0 2 L 50 2 L 50 0 Z "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</svg>
|
||
</div>
|
||
<div class="numeral"><span style="color: #A23B72;">3</span><span style="color: #2E86AB;">5</span></div>
|
||
</div>
|
||
<div class="flashcard" data-number="36">
|
||
<div class="card-number">#36</div>
|
||
<div class="abacus-container">
|
||
<svg class="typst-doc" viewBox="0 0 108 108" width="108pt" height="108pt" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:h5="http://www.w3.org/1999/xhtml">
|
||
<g>
|
||
<g transform="translate(5.400000000000001 5.400000000000001)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(23.599999999999998 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 -11.400000000000002)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(11 10)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 80 L 3 80 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(6.5 10)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 33)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 45.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 58)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 78)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(36 17)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 73 L 3 73 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(31.5 17)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 33)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 53)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 65.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 78)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(0 30)">
|
||
<path class="typst-shape" fill="#000000" fill-rule="nonzero" d="M 0 0 L 0 2 L 50 2 L 50 0 Z "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</svg>
|
||
</div>
|
||
<div class="numeral"><span style="color: #A23B72;">3</span><span style="color: #2E86AB;">6</span></div>
|
||
</div>
|
||
<div class="flashcard" data-number="37">
|
||
<div class="card-number">#37</div>
|
||
<div class="abacus-container">
|
||
<svg class="typst-doc" viewBox="0 0 108 108" width="108pt" height="108pt" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:h5="http://www.w3.org/1999/xhtml">
|
||
<g>
|
||
<g transform="translate(5.400000000000001 5.400000000000001)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(23.599999999999998 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 -11.400000000000002)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(11 10)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 80 L 3 80 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(6.5 10)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 33)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 45.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 58)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 78)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(36 17)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 73 L 3 73 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(31.5 17)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 33)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 45.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 65.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 78)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(0 30)">
|
||
<path class="typst-shape" fill="#000000" fill-rule="nonzero" d="M 0 0 L 0 2 L 50 2 L 50 0 Z "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</svg>
|
||
</div>
|
||
<div class="numeral"><span style="color: #A23B72;">3</span><span style="color: #2E86AB;">7</span></div>
|
||
</div>
|
||
<div class="flashcard" data-number="38">
|
||
<div class="card-number">#38</div>
|
||
<div class="abacus-container">
|
||
<svg class="typst-doc" viewBox="0 0 108 108" width="108pt" height="108pt" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:h5="http://www.w3.org/1999/xhtml">
|
||
<g>
|
||
<g transform="translate(5.400000000000001 5.400000000000001)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(23.599999999999998 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 -11.400000000000002)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(11 10)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 80 L 3 80 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(6.5 10)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 33)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 45.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 58)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 78)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(36 17)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 73 L 3 73 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(31.5 17)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 33)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 45.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 58)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 78)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(0 30)">
|
||
<path class="typst-shape" fill="#000000" fill-rule="nonzero" d="M 0 0 L 0 2 L 50 2 L 50 0 Z "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</svg>
|
||
</div>
|
||
<div class="numeral"><span style="color: #A23B72;">3</span><span style="color: #2E86AB;">8</span></div>
|
||
</div>
|
||
<div class="flashcard" data-number="39">
|
||
<div class="card-number">#39</div>
|
||
<div class="abacus-container">
|
||
<svg class="typst-doc" viewBox="0 0 108 108" width="108pt" height="108pt" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:h5="http://www.w3.org/1999/xhtml">
|
||
<g>
|
||
<g transform="translate(5.400000000000001 5.400000000000001)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(23.599999999999998 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 -11.400000000000002)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(11 10)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 80 L 3 80 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(6.5 10)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 33)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 45.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 58)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 78)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(36 17)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 65.5 L 3 65.5 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(31.5 17)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 33)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 45.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 58)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 70.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(0 30)">
|
||
<path class="typst-shape" fill="#000000" fill-rule="nonzero" d="M 0 0 L 0 2 L 50 2 L 50 0 Z "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</svg>
|
||
</div>
|
||
<div class="numeral"><span style="color: #A23B72;">3</span><span style="color: #2E86AB;">9</span></div>
|
||
</div>
|
||
<div class="flashcard" data-number="40">
|
||
<div class="card-number">#40</div>
|
||
<div class="abacus-container">
|
||
<svg class="typst-doc" viewBox="0 0 108 108" width="108pt" height="108pt" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:h5="http://www.w3.org/1999/xhtml">
|
||
<g>
|
||
<g transform="translate(5.400000000000001 5.400000000000001)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(23.599999999999998 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 -11.400000000000002)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(11 10)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 72.5 L 3 72.5 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(6.5 10)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 33)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 45.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 58)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 70.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(36 10)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 79.5 L 3 79.5 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(31.5 10)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 40)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 52.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 65)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 77.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(0 30)">
|
||
<path class="typst-shape" fill="#000000" fill-rule="nonzero" d="M 0 0 L 0 2 L 50 2 L 50 0 Z "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</svg>
|
||
</div>
|
||
<div class="numeral"><span style="color: #A23B72;">4</span><span style="color: #2E86AB;">0</span></div>
|
||
</div>
|
||
<div class="flashcard" data-number="41">
|
||
<div class="card-number">#41</div>
|
||
<div class="abacus-container">
|
||
<svg class="typst-doc" viewBox="0 0 108 108" width="108pt" height="108pt" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:h5="http://www.w3.org/1999/xhtml">
|
||
<g>
|
||
<g transform="translate(5.400000000000001 5.400000000000001)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(23.599999999999998 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 -11.400000000000002)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(11 10)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 72.5 L 3 72.5 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(6.5 10)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 33)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 45.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 58)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 70.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(36 10)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 80 L 3 80 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(31.5 10)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 33)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 53)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 65.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 78)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(0 30)">
|
||
<path class="typst-shape" fill="#000000" fill-rule="nonzero" d="M 0 0 L 0 2 L 50 2 L 50 0 Z "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</svg>
|
||
</div>
|
||
<div class="numeral"><span style="color: #A23B72;">4</span><span style="color: #2E86AB;">1</span></div>
|
||
</div>
|
||
<div class="flashcard" data-number="42">
|
||
<div class="card-number">#42</div>
|
||
<div class="abacus-container">
|
||
<svg class="typst-doc" viewBox="0 0 108 108" width="108pt" height="108pt" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:h5="http://www.w3.org/1999/xhtml">
|
||
<g>
|
||
<g transform="translate(5.400000000000001 5.400000000000001)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(23.599999999999998 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 -11.400000000000002)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(11 10)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 72.5 L 3 72.5 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(6.5 10)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 33)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 45.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 58)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 70.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(36 10)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 80 L 3 80 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(31.5 10)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 33)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 45.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 65.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 78)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(0 30)">
|
||
<path class="typst-shape" fill="#000000" fill-rule="nonzero" d="M 0 0 L 0 2 L 50 2 L 50 0 Z "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</svg>
|
||
</div>
|
||
<div class="numeral"><span style="color: #A23B72;">4</span><span style="color: #2E86AB;">2</span></div>
|
||
</div>
|
||
<div class="flashcard" data-number="43">
|
||
<div class="card-number">#43</div>
|
||
<div class="abacus-container">
|
||
<svg class="typst-doc" viewBox="0 0 108 108" width="108pt" height="108pt" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:h5="http://www.w3.org/1999/xhtml">
|
||
<g>
|
||
<g transform="translate(5.400000000000001 5.400000000000001)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(23.599999999999998 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 -11.400000000000002)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(11 10)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 72.5 L 3 72.5 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(6.5 10)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 33)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 45.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 58)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 70.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(36 10)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 80 L 3 80 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(31.5 10)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 33)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 45.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 58)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 78)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(0 30)">
|
||
<path class="typst-shape" fill="#000000" fill-rule="nonzero" d="M 0 0 L 0 2 L 50 2 L 50 0 Z "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</svg>
|
||
</div>
|
||
<div class="numeral"><span style="color: #A23B72;">4</span><span style="color: #2E86AB;">3</span></div>
|
||
</div>
|
||
<div class="flashcard" data-number="44">
|
||
<div class="card-number">#44</div>
|
||
<div class="abacus-container">
|
||
<svg class="typst-doc" viewBox="0 0 108 108" width="108pt" height="108pt" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:h5="http://www.w3.org/1999/xhtml">
|
||
<g>
|
||
<g transform="translate(5.400000000000001 5.400000000000001)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(23.599999999999998 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 -11.400000000000002)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(11 10)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 72.5 L 3 72.5 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(6.5 10)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 33)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 45.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 58)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 70.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(36 10)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 72.5 L 3 72.5 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(31.5 10)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 33)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 45.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 58)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 70.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(0 30)">
|
||
<path class="typst-shape" fill="#000000" fill-rule="nonzero" d="M 0 0 L 0 2 L 50 2 L 50 0 Z "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</svg>
|
||
</div>
|
||
<div class="numeral"><span style="color: #A23B72;">4</span><span style="color: #2E86AB;">4</span></div>
|
||
</div>
|
||
<div class="flashcard" data-number="45">
|
||
<div class="card-number">#45</div>
|
||
<div class="abacus-container">
|
||
<svg class="typst-doc" viewBox="0 0 108 108" width="108pt" height="108pt" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:h5="http://www.w3.org/1999/xhtml">
|
||
<g>
|
||
<g transform="translate(5.400000000000001 5.400000000000001)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(23.599999999999998 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 -11.400000000000002)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(11 10)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 72.5 L 3 72.5 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(6.5 10)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 33)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 45.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 58)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 70.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(36 17)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 72.5 L 3 72.5 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(31.5 17)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 40)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 52.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 65)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 77.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(0 30)">
|
||
<path class="typst-shape" fill="#000000" fill-rule="nonzero" d="M 0 0 L 0 2 L 50 2 L 50 0 Z "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</svg>
|
||
</div>
|
||
<div class="numeral"><span style="color: #A23B72;">4</span><span style="color: #2E86AB;">5</span></div>
|
||
</div>
|
||
<div class="flashcard" data-number="46">
|
||
<div class="card-number">#46</div>
|
||
<div class="abacus-container">
|
||
<svg class="typst-doc" viewBox="0 0 108 108" width="108pt" height="108pt" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:h5="http://www.w3.org/1999/xhtml">
|
||
<g>
|
||
<g transform="translate(5.400000000000001 5.400000000000001)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(23.599999999999998 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 -11.400000000000002)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(11 10)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 72.5 L 3 72.5 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(6.5 10)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 33)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 45.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 58)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 70.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(36 17)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 73 L 3 73 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(31.5 17)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 33)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 53)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 65.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 78)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(0 30)">
|
||
<path class="typst-shape" fill="#000000" fill-rule="nonzero" d="M 0 0 L 0 2 L 50 2 L 50 0 Z "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</svg>
|
||
</div>
|
||
<div class="numeral"><span style="color: #A23B72;">4</span><span style="color: #2E86AB;">6</span></div>
|
||
</div>
|
||
<div class="flashcard" data-number="47">
|
||
<div class="card-number">#47</div>
|
||
<div class="abacus-container">
|
||
<svg class="typst-doc" viewBox="0 0 108 108" width="108pt" height="108pt" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:h5="http://www.w3.org/1999/xhtml">
|
||
<g>
|
||
<g transform="translate(5.400000000000001 5.400000000000001)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(23.599999999999998 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 -11.400000000000002)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(11 10)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 72.5 L 3 72.5 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(6.5 10)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 33)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 45.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 58)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 70.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(36 17)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 73 L 3 73 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(31.5 17)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 33)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 45.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 65.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 78)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(0 30)">
|
||
<path class="typst-shape" fill="#000000" fill-rule="nonzero" d="M 0 0 L 0 2 L 50 2 L 50 0 Z "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</svg>
|
||
</div>
|
||
<div class="numeral"><span style="color: #A23B72;">4</span><span style="color: #2E86AB;">7</span></div>
|
||
</div>
|
||
<div class="flashcard" data-number="48">
|
||
<div class="card-number">#48</div>
|
||
<div class="abacus-container">
|
||
<svg class="typst-doc" viewBox="0 0 108 108" width="108pt" height="108pt" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:h5="http://www.w3.org/1999/xhtml">
|
||
<g>
|
||
<g transform="translate(5.400000000000001 5.400000000000001)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(23.599999999999998 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 -11.400000000000002)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(11 10)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 72.5 L 3 72.5 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(6.5 10)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 33)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 45.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 58)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 70.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(36 17)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 73 L 3 73 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(31.5 17)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 33)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 45.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 58)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 78)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(0 30)">
|
||
<path class="typst-shape" fill="#000000" fill-rule="nonzero" d="M 0 0 L 0 2 L 50 2 L 50 0 Z "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</svg>
|
||
</div>
|
||
<div class="numeral"><span style="color: #A23B72;">4</span><span style="color: #2E86AB;">8</span></div>
|
||
</div>
|
||
<div class="flashcard" data-number="49">
|
||
<div class="card-number">#49</div>
|
||
<div class="abacus-container">
|
||
<svg class="typst-doc" viewBox="0 0 108 108" width="108pt" height="108pt" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:h5="http://www.w3.org/1999/xhtml">
|
||
<g>
|
||
<g transform="translate(5.400000000000001 5.400000000000001)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(23.599999999999998 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 -11.400000000000002)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(11 10)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 72.5 L 3 72.5 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(6.5 10)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 33)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 45.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 58)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 70.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(36 17)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 65.5 L 3 65.5 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(31.5 17)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 33)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 45.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 58)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 70.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#2e86ab" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(0 30)">
|
||
<path class="typst-shape" fill="#000000" fill-rule="nonzero" d="M 0 0 L 0 2 L 50 2 L 50 0 Z "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</svg>
|
||
</div>
|
||
<div class="numeral"><span style="color: #A23B72;">4</span><span style="color: #2E86AB;">9</span></div>
|
||
</div>
|
||
<div class="flashcard" data-number="50">
|
||
<div class="card-number">#50</div>
|
||
<div class="abacus-container">
|
||
<svg class="typst-doc" viewBox="0 0 108 108" width="108pt" height="108pt" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:h5="http://www.w3.org/1999/xhtml">
|
||
<g>
|
||
<g transform="translate(5.400000000000001 5.400000000000001)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(23.599999999999998 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 -11.400000000000002)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(0 0)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(11 17)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 72.5 L 3 72.5 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(6.5 17)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#a23b72" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 40)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 52.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 65)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(6.5 77.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(36 10)">
|
||
<path class="typst-shape" fill="#eeeeee" fill-rule="nonzero" d="M 0 0 L 0 79.5 L 3 79.5 L 3 0 Z "/>
|
||
</g>
|
||
<g transform="translate(31.5 10)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 40)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 52.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 65)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(31.5 77.5)">
|
||
<g class="typst-group">
|
||
<g>
|
||
<g transform="translate(-0 -0)">
|
||
<path class="typst-shape" fill="#e6e6e6" fill-rule="nonzero" stroke="#000000" stroke-width="0.5" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="4" d="M 0 6 C 0 2.689296 2.689296 0 6 0 C 9.310704 0 12 2.689296 12 6 C 12 9.310704 9.310704 12 6 12 C 2.689296 12 0 9.310704 0 6 "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
<g transform="translate(0 30)">
|
||
<path class="typst-shape" fill="#000000" fill-rule="nonzero" d="M 0 0 L 0 2 L 50 2 L 50 0 Z "/>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</g>
|
||
</svg>
|
||
</div>
|
||
<div class="numeral"><span style="color: #A23B72;">5</span><span style="color: #2E86AB;">0</span></div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="instructions">
|
||
<p><em>Interactive flashcards for digital learning. Use the left/right arrow keys or click the cards to flip them.</em></p>
|
||
</div>
|
||
</section>
|
||
|
||
</div> <!-- End Content Area -->
|
||
</div>
|
||
|
||
<script>
|
||
// Modal Manager - Handles modal dialogs and fullscreen functionality
|
||
class ModalManager {
|
||
constructor() {
|
||
this.isFullscreen = false;
|
||
this.bindEvents();
|
||
}
|
||
|
||
bindEvents() {
|
||
// Challenge button events
|
||
document.getElementById('open-quiz-modal').addEventListener('click', () => {
|
||
this.openModal('quiz-modal');
|
||
});
|
||
|
||
document.getElementById('open-sorting-modal').addEventListener('click', () => {
|
||
this.openModal('sorting-modal');
|
||
});
|
||
|
||
document.getElementById('open-matching-modal').addEventListener('click', () => {
|
||
this.openModal('matching-modal');
|
||
});
|
||
|
||
document.getElementById('open-complement-modal').addEventListener('click', () => {
|
||
this.openModal('complement-modal');
|
||
});
|
||
|
||
// Close button events
|
||
document.getElementById('close-quiz-modal').addEventListener('click', () => {
|
||
this.closeModal('quiz-modal');
|
||
});
|
||
|
||
document.getElementById('close-sorting-modal').addEventListener('click', () => {
|
||
this.closeModal('sorting-modal');
|
||
});
|
||
|
||
document.getElementById('close-matching-modal').addEventListener('click', () => {
|
||
this.closeModal('matching-modal');
|
||
});
|
||
|
||
document.getElementById('close-complement-modal').addEventListener('click', () => {
|
||
this.closeModal('complement-modal');
|
||
});
|
||
|
||
// Fullscreen button events
|
||
document.getElementById('quiz-fullscreen-btn').addEventListener('click', () => {
|
||
this.toggleFullscreen('quiz-modal');
|
||
});
|
||
|
||
document.getElementById('sorting-fullscreen-btn').addEventListener('click', () => {
|
||
this.toggleFullscreen('sorting-modal');
|
||
});
|
||
|
||
document.getElementById('matching-fullscreen-btn').addEventListener('click', () => {
|
||
this.toggleFullscreen('matching-modal');
|
||
});
|
||
|
||
document.getElementById('complement-fullscreen-btn').addEventListener('click', () => {
|
||
this.toggleFullscreen('complement-modal');
|
||
});
|
||
|
||
// Close modal when clicking outside
|
||
document.addEventListener('click', (e) => {
|
||
if (e.target.classList.contains('modal') && e.target.classList.contains('show')) {
|
||
this.closeModal(e.target.id);
|
||
}
|
||
});
|
||
|
||
// ESC key to close modal
|
||
document.addEventListener('keydown', (e) => {
|
||
if (e.key === 'Escape') {
|
||
const openModal = document.querySelector('.modal.show');
|
||
if (openModal) {
|
||
this.closeModal(openModal.id);
|
||
}
|
||
}
|
||
});
|
||
|
||
// Fullscreen change events
|
||
document.addEventListener('fullscreenchange', () => {
|
||
this.handleFullscreenChange();
|
||
});
|
||
|
||
document.addEventListener('webkitfullscreenchange', () => {
|
||
this.handleFullscreenChange();
|
||
});
|
||
|
||
document.addEventListener('mozfullscreenchange', () => {
|
||
this.handleFullscreenChange();
|
||
});
|
||
|
||
document.addEventListener('MSFullscreenChange', () => {
|
||
this.handleFullscreenChange();
|
||
});
|
||
}
|
||
|
||
openModal(modalId) {
|
||
const modal = document.getElementById(modalId);
|
||
modal.classList.add('show');
|
||
document.body.style.overflow = 'hidden';
|
||
}
|
||
|
||
closeModal(modalId) {
|
||
const modal = document.getElementById(modalId);
|
||
if (this.isFullscreen) {
|
||
this.exitFullscreen();
|
||
}
|
||
modal.classList.remove('show', 'fullscreen');
|
||
document.body.style.overflow = '';
|
||
|
||
// Stop active games when their modals are closed
|
||
if (modalId === 'complement-modal' && this.complementRace && this.complementRace.isGameActive) {
|
||
console.log('🛑 Stopping active complement race due to modal close');
|
||
this.complementRace.endRace('Game stopped - modal closed');
|
||
} else if (modalId === 'matching-modal' && this.matchingGame && this.matchingGame.gameActive) {
|
||
console.log('🛑 Stopping active matching game due to modal close');
|
||
this.matchingGame.endGame();
|
||
} else if (modalId === 'sorting-modal' && this.sortingGame && this.sortingGame.gameActive) {
|
||
console.log('🛑 Stopping active sorting game due to modal close');
|
||
this.sortingGame.endGame();
|
||
} else if (modalId === 'quiz-modal' && this.quizGame && this.quizGame.gameActive) {
|
||
console.log('🛑 Stopping active quiz game due to modal close');
|
||
this.quizGame.endGame();
|
||
}
|
||
}
|
||
|
||
async toggleFullscreen(modalId) {
|
||
const modal = document.getElementById(modalId);
|
||
|
||
if (!this.isFullscreen) {
|
||
try {
|
||
if (modal.requestFullscreen) {
|
||
await modal.requestFullscreen();
|
||
} else if (modal.webkitRequestFullscreen) {
|
||
await modal.webkitRequestFullscreen();
|
||
} else if (modal.mozRequestFullScreen) {
|
||
await modal.mozRequestFullScreen();
|
||
} else if (modal.msRequestFullscreen) {
|
||
await modal.msRequestFullscreen();
|
||
}
|
||
modal.classList.add('fullscreen');
|
||
} catch (error) {
|
||
console.warn('Fullscreen not supported or failed:', error);
|
||
// Fallback to CSS fullscreen
|
||
modal.classList.add('fullscreen');
|
||
}
|
||
} else {
|
||
this.exitFullscreen();
|
||
}
|
||
}
|
||
|
||
exitFullscreen() {
|
||
if (document.exitFullscreen) {
|
||
document.exitFullscreen();
|
||
} else if (document.webkitExitFullscreen) {
|
||
document.webkitExitFullscreen();
|
||
} else if (document.mozCancelFullScreen) {
|
||
document.mozCancelFullScreen();
|
||
} else if (document.msExitFullscreen) {
|
||
document.msExitFullscreen();
|
||
}
|
||
}
|
||
|
||
handleFullscreenChange() {
|
||
const isFullscreen = !!(document.fullscreenElement ||
|
||
document.webkitFullscreenElement ||
|
||
document.mozFullScreenElement ||
|
||
document.msFullscreenElement);
|
||
|
||
this.isFullscreen = isFullscreen;
|
||
|
||
if (!isFullscreen) {
|
||
// Remove fullscreen class from all modals when exiting fullscreen
|
||
document.querySelectorAll('.modal').forEach(modal => {
|
||
modal.classList.remove('fullscreen');
|
||
});
|
||
}
|
||
}
|
||
}
|
||
|
||
// Quiz functionality - No dependencies, pure JavaScript
|
||
class SorobanQuiz {
|
||
constructor() {
|
||
this.cards = [];
|
||
this.quizCards = [];
|
||
this.currentCardIndex = 0;
|
||
this.displayTime = 2.0;
|
||
this.selectedCount = 15;
|
||
this.foundNumbers = [];
|
||
this.correctAnswers = [];
|
||
this.guessesRemaining = 0;
|
||
this.currentInput = '';
|
||
this.incorrectGuesses = 0;
|
||
this.finishButtonsBound = false;
|
||
this.prefixAcceptanceTimeout = null; // Timeout management for prefix conflicts
|
||
|
||
this.initializeCards();
|
||
this.bindEvents();
|
||
}
|
||
|
||
initializeCards() {
|
||
// Extract card data from the DOM
|
||
const cardElements = document.querySelectorAll('.flashcard');
|
||
this.cards = Array.from(cardElements).map(card => ({
|
||
number: parseInt(card.dataset.number),
|
||
svg: card.querySelector('.abacus-container').innerHTML,
|
||
element: card
|
||
}));
|
||
}
|
||
|
||
bindEvents() {
|
||
// Count buttons
|
||
document.querySelectorAll('.count-btn').forEach(btn => {
|
||
btn.addEventListener('click', (e) => {
|
||
document.querySelectorAll('.count-btn').forEach(b => b.classList.remove('active'));
|
||
btn.classList.add('active');
|
||
this.selectedCount = btn.dataset.count === 'all' ? this.cards.length : parseInt(btn.dataset.count);
|
||
});
|
||
});
|
||
|
||
// Display time slider
|
||
const slider = document.getElementById('display-time');
|
||
const valueDisplay = document.querySelector('.slider-value');
|
||
slider.addEventListener('input', (e) => {
|
||
this.displayTime = parseFloat(e.target.value);
|
||
valueDisplay.textContent = this.displayTime.toFixed(1) + 's';
|
||
});
|
||
|
||
// Start quiz button
|
||
document.getElementById('start-quiz').addEventListener('click', () => {
|
||
this.startQuiz();
|
||
});
|
||
|
||
// Note: Submit answers button replaced by smart input system
|
||
|
||
// Note: Finish quiz buttons are bound later in showInputPhase() when they become visible
|
||
|
||
// End quiz button
|
||
document.getElementById('end-quiz').addEventListener('click', () => {
|
||
this.endQuiz();
|
||
});
|
||
|
||
// Retry and back buttons
|
||
document.getElementById('retry-quiz').addEventListener('click', () => {
|
||
this.resetQuiz();
|
||
this.startQuiz();
|
||
});
|
||
|
||
document.getElementById('back-to-cards').addEventListener('click', () => {
|
||
this.resetQuiz();
|
||
});
|
||
}
|
||
|
||
async startQuiz() {
|
||
// Select random cards
|
||
this.quizCards = this.getRandomCards(this.selectedCount);
|
||
this.correctAnswers = this.quizCards.map(card => card.number);
|
||
this.currentCardIndex = 0;
|
||
|
||
// Hide configuration controls, show only game
|
||
document.querySelector('.quiz-controls').style.display = 'none';
|
||
|
||
// Show quiz game section within modal
|
||
this.hideQuizSections();
|
||
document.getElementById('quiz-game').style.display = 'block';
|
||
document.getElementById('total-cards').textContent = this.quizCards.length;
|
||
|
||
// Start with the first card
|
||
this.showNextCard();
|
||
}
|
||
|
||
getRandomCards(count) {
|
||
const shuffled = [...this.cards].sort(() => 0.5 - Math.random());
|
||
return shuffled.slice(0, Math.min(count, this.cards.length));
|
||
}
|
||
|
||
async showNextCard() {
|
||
if (this.currentCardIndex >= this.quizCards.length) {
|
||
this.showInputPhase();
|
||
return;
|
||
}
|
||
|
||
const card = this.quizCards[this.currentCardIndex];
|
||
const progress = ((this.currentCardIndex) / this.quizCards.length) * 100;
|
||
|
||
// Update progress
|
||
document.querySelector('.progress-fill').style.width = progress + '%';
|
||
document.getElementById('current-card').textContent = this.currentCardIndex + 1;
|
||
|
||
// Only show countdown for the very first card
|
||
if (this.currentCardIndex === 0) {
|
||
await this.showCountdown();
|
||
} else {
|
||
// Subtle "new card" indicator for subsequent cards
|
||
await this.showNewCardIndicator();
|
||
}
|
||
|
||
// Show card
|
||
await this.displayCard(card);
|
||
|
||
this.currentCardIndex++;
|
||
|
||
// Minimal delay before next card (just enough for the exit animation)
|
||
setTimeout(() => {
|
||
this.showNextCard();
|
||
}, 100);
|
||
}
|
||
|
||
async showNewCardIndicator() {
|
||
return new Promise(resolve => {
|
||
const countdownEl = document.getElementById('quiz-countdown');
|
||
const cardEl = document.getElementById('quiz-card');
|
||
|
||
// Hide card temporarily
|
||
cardEl.style.display = 'none';
|
||
countdownEl.style.display = 'block';
|
||
|
||
// Brief flash to indicate new card
|
||
countdownEl.textContent = 'Next';
|
||
countdownEl.className = 'quiz-countdown new-card-flash';
|
||
|
||
setTimeout(() => {
|
||
countdownEl.style.display = 'none';
|
||
resolve();
|
||
}, 150); // Very brief indication
|
||
});
|
||
}
|
||
|
||
async showCountdown() {
|
||
const countdownEl = document.getElementById('quiz-countdown');
|
||
const cardEl = document.getElementById('quiz-card');
|
||
|
||
cardEl.style.visibility = 'hidden';
|
||
countdownEl.style.display = 'block';
|
||
|
||
// 3, 2, 1 countdown
|
||
const counts = ['3', '2', '1', 'GO!'];
|
||
|
||
for (let i = 0; i < counts.length; i++) {
|
||
countdownEl.textContent = counts[i];
|
||
countdownEl.className = 'countdown';
|
||
if (i === counts.length - 1) countdownEl.classList.add('go');
|
||
|
||
await this.delay(400);
|
||
}
|
||
|
||
countdownEl.style.display = 'none';
|
||
}
|
||
|
||
async displayCard(card) {
|
||
const cardEl = document.getElementById('quiz-card');
|
||
const countdownEl = document.getElementById('quiz-countdown');
|
||
|
||
// Show card content with entry animation
|
||
cardEl.innerHTML = card.svg;
|
||
cardEl.style.display = 'block';
|
||
cardEl.style.visibility = 'visible';
|
||
cardEl.classList.add('pulse');
|
||
|
||
// Display for most of the time
|
||
await this.delay((this.displayTime * 1000) - 300);
|
||
|
||
// Subtle exit signal - brief red border flash
|
||
cardEl.classList.add('card-exit-warning');
|
||
await this.delay(200);
|
||
|
||
// Quick fade out
|
||
cardEl.classList.add('card-fade-out');
|
||
await this.delay(100);
|
||
|
||
// Hide card and reset classes
|
||
cardEl.classList.remove('pulse', 'card-exit-warning', 'card-fade-out');
|
||
cardEl.style.visibility = 'hidden';
|
||
}
|
||
|
||
showInputPhase() {
|
||
// Complete progress bar
|
||
document.querySelector('.progress-fill').style.width = '100%';
|
||
|
||
// Initialize smart input system
|
||
this.correctAnswers = this.quizCards.map(card => card.number);
|
||
this.foundNumbers = [];
|
||
this.guessesRemaining = this.selectedCount + Math.floor(this.selectedCount / 2); // Allow 50% extra guesses
|
||
|
||
// Update stats display
|
||
document.getElementById('cards-shown-count').textContent = this.quizCards.length;
|
||
document.getElementById('guesses-remaining').textContent = this.guessesRemaining;
|
||
document.getElementById('numbers-found').textContent = '0';
|
||
|
||
// Hide quiz game, show input
|
||
this.hideQuizSections();
|
||
document.getElementById('quiz-input').style.display = 'block';
|
||
|
||
// Setup smart input
|
||
const smartInput = document.getElementById('smart-input');
|
||
const display = document.getElementById('number-display');
|
||
smartInput.value = '';
|
||
document.getElementById('current-typing').textContent = '';
|
||
|
||
// Focus the hidden input and make sure it captures keyboard events
|
||
smartInput.focus();
|
||
|
||
// Remove any existing event listeners to prevent duplicates
|
||
const newSmartInput = smartInput.cloneNode(true);
|
||
smartInput.parentNode.replaceChild(newSmartInput, smartInput);
|
||
|
||
// Add input event listener for real-time validation
|
||
newSmartInput.addEventListener('input', (e) => this.handleSmartInput(e));
|
||
|
||
// Make the display area clickable to maintain focus
|
||
display.addEventListener('click', () => {
|
||
newSmartInput.focus();
|
||
});
|
||
|
||
// Keep focus on the hidden input
|
||
newSmartInput.focus();
|
||
|
||
// Bind finish buttons (they exist now that quiz-input is shown)
|
||
this.bindFinishButtons();
|
||
|
||
// Show finish button when all numbers found or guesses exhausted
|
||
this.updateFinishButtonVisibility();
|
||
}
|
||
|
||
bindFinishButtons() {
|
||
// Bind finish quiz buttons - called when input phase starts and buttons are visible
|
||
// Only bind once to prevent duplicate listeners
|
||
if (this.finishButtonsBound) return;
|
||
|
||
const finishBtn = document.getElementById('finish-quiz');
|
||
const giveUpBtn = document.getElementById('give-up-quiz');
|
||
|
||
if (finishBtn) {
|
||
finishBtn.addEventListener('click', () => {
|
||
console.log('Finish quiz button clicked');
|
||
this.finishQuiz();
|
||
});
|
||
console.log('Finish button event listener added');
|
||
} else {
|
||
console.error('finish-quiz button not found in DOM');
|
||
}
|
||
|
||
if (giveUpBtn) {
|
||
giveUpBtn.addEventListener('click', () => {
|
||
console.log('Give up button clicked');
|
||
this.finishQuiz();
|
||
});
|
||
console.log('Give up button event listener added');
|
||
} else {
|
||
console.error('give-up-quiz button not found in DOM');
|
||
}
|
||
|
||
this.finishButtonsBound = true;
|
||
}
|
||
|
||
handleSmartInput(event) {
|
||
const input = event.target;
|
||
const value = input.value.trim();
|
||
const display = document.getElementById('number-display');
|
||
const typingSpan = document.getElementById('current-typing');
|
||
|
||
// Always clear any existing timeout first
|
||
if (this.prefixAcceptanceTimeout) {
|
||
clearTimeout(this.prefixAcceptanceTimeout);
|
||
this.prefixAcceptanceTimeout = null;
|
||
}
|
||
|
||
// Reset visual feedback
|
||
display.classList.remove('correct', 'incorrect');
|
||
|
||
// Update the visual display
|
||
typingSpan.textContent = value;
|
||
|
||
// Check if input is empty
|
||
if (!value) {
|
||
this.currentInput = '';
|
||
return;
|
||
}
|
||
|
||
// Check if it's a valid number
|
||
const number = parseInt(value);
|
||
if (isNaN(number)) {
|
||
return; // Wait for more input
|
||
}
|
||
|
||
this.currentInput = value;
|
||
|
||
// Check if this number is in our correct answers and not already found
|
||
if (this.correctAnswers.includes(number) && !this.foundNumbers.includes(number)) {
|
||
// Check if this input is a prefix of any other correct answers
|
||
if (!this.isPrefix(value, this.correctAnswers)) {
|
||
// Safe to auto-accept immediately - no timeout needed
|
||
this.acceptCorrectNumber(number, input, display);
|
||
} else {
|
||
// Brief delay for potential prefixes - store timeout ID
|
||
this.prefixAcceptanceTimeout = setTimeout(() => {
|
||
this.prefixAcceptanceTimeout = null; // Clear reference
|
||
this.acceptCorrectNumber(number, input, display);
|
||
}, 500);
|
||
}
|
||
} else if (value.length >= 2 && !this.correctAnswers.includes(number)) {
|
||
// Wrong number (only trigger after at least 2 digits to avoid false positives)
|
||
this.handleIncorrectGuess(input, display);
|
||
}
|
||
}
|
||
|
||
isPrefix(input, numbers) {
|
||
return numbers.some(n => n.toString().startsWith(input) && n.toString() !== input);
|
||
}
|
||
|
||
acceptCorrectNumber(number, input, display) {
|
||
// Add to found numbers
|
||
this.foundNumbers.push(number);
|
||
|
||
// Visual success feedback
|
||
display.classList.add('correct');
|
||
|
||
// Update stats
|
||
document.getElementById('numbers-found').textContent = this.foundNumbers.length;
|
||
|
||
// Add to found numbers display
|
||
this.addFoundNumberDisplay(number);
|
||
|
||
// Clear input immediately for fast entry
|
||
setTimeout(() => {
|
||
input.value = '';
|
||
document.getElementById('current-typing').textContent = '';
|
||
this.currentInput = '';
|
||
|
||
// Check if we're done
|
||
this.updateFinishButtonVisibility();
|
||
|
||
// If all numbers found, auto-finish
|
||
if (this.foundNumbers.length === this.correctAnswers.length) {
|
||
setTimeout(() => this.finishQuiz(), 1000);
|
||
}
|
||
}, 150); // Much shorter delay - just enough to show the success feedback
|
||
|
||
// Remove success visual feedback after animation completes
|
||
setTimeout(() => {
|
||
display.classList.remove('correct');
|
||
}, 500);
|
||
}
|
||
|
||
handleIncorrectGuess(input, display) {
|
||
// Only penalize if we have guesses remaining
|
||
if (this.guessesRemaining > 0) {
|
||
this.guessesRemaining--;
|
||
this.incorrectGuesses++; // Track incorrect guesses for scoring
|
||
document.getElementById('guesses-remaining').textContent = this.guessesRemaining;
|
||
|
||
// Visual error feedback
|
||
display.classList.add('incorrect');
|
||
|
||
// Clear input quickly for rapid entry
|
||
setTimeout(() => {
|
||
input.value = '';
|
||
document.getElementById('current-typing').textContent = '';
|
||
this.currentInput = '';
|
||
|
||
// Check if we're out of guesses
|
||
this.updateFinishButtonVisibility();
|
||
if (this.guessesRemaining === 0) {
|
||
setTimeout(() => this.finishQuiz(), 1000);
|
||
}
|
||
}, 150); // Same fast clearing as correct numbers
|
||
|
||
// Remove error visual feedback after animation completes
|
||
setTimeout(() => {
|
||
display.classList.remove('incorrect');
|
||
}, 500);
|
||
}
|
||
}
|
||
|
||
addFoundNumberDisplay(number) {
|
||
const foundContainer = document.getElementById('found-numbers');
|
||
const numberElement = document.createElement('span');
|
||
numberElement.className = 'found-number';
|
||
numberElement.textContent = number;
|
||
foundContainer.appendChild(numberElement);
|
||
}
|
||
|
||
updateFinishButtonVisibility() {
|
||
const finishBtn = document.getElementById('finish-quiz');
|
||
const giveUpBtn = document.getElementById('give-up-quiz');
|
||
|
||
const hasFoundSome = this.foundNumbers.length > 0;
|
||
const hasFoundAll = this.foundNumbers.length === this.correctAnswers.length;
|
||
const outOfGuesses = this.guessesRemaining === 0;
|
||
const hasGuessesLeft = this.guessesRemaining > 0;
|
||
|
||
if (hasFoundAll || outOfGuesses) {
|
||
// Show finish button when all found or no guesses left
|
||
finishBtn.style.display = 'block';
|
||
giveUpBtn.style.display = 'none';
|
||
finishBtn.textContent = hasFoundAll ? 'Finish Quiz' : 'Show Results';
|
||
} else if (hasFoundSome && hasGuessesLeft) {
|
||
// Show both buttons when user has found some but could find more
|
||
finishBtn.style.display = 'block';
|
||
giveUpBtn.style.display = 'block';
|
||
finishBtn.textContent = 'Show Results';
|
||
} else {
|
||
// No buttons when user hasn't found any yet
|
||
finishBtn.style.display = 'none';
|
||
giveUpBtn.style.display = 'none';
|
||
}
|
||
}
|
||
|
||
finishQuiz() {
|
||
console.log('finishQuiz called, foundNumbers:', this.foundNumbers);
|
||
// Use found numbers as answers and show results
|
||
this.answers = [...this.foundNumbers];
|
||
console.log('About to call showResults with answers:', this.answers);
|
||
this.showResults();
|
||
}
|
||
|
||
submitAnswers() {
|
||
const input = document.getElementById('answer-input').value;
|
||
this.answers = this.parseAnswers(input);
|
||
this.showResults();
|
||
}
|
||
|
||
parseAnswers(input) {
|
||
// Parse comma or space separated numbers
|
||
return input.split(/[,\s]+/)
|
||
.map(s => s.trim())
|
||
.filter(s => s.length > 0)
|
||
.map(s => parseInt(s))
|
||
.filter(n => !isNaN(n));
|
||
}
|
||
|
||
showResults() {
|
||
const scoreData = this.calculateScore();
|
||
const correct = scoreData.correct;
|
||
const finalScore = scoreData.finalScore;
|
||
const percentage = Math.round(finalScore);
|
||
|
||
// Update score display
|
||
document.getElementById('score-percentage').textContent = percentage + '%';
|
||
document.getElementById('score-correct').textContent = correct.length;
|
||
document.getElementById('score-total').textContent = this.correctAnswers.length;
|
||
document.getElementById('result-timing').textContent = this.displayTime.toFixed(1) + 's';
|
||
|
||
// Show detailed results with penalty info
|
||
this.showDetailedResults(correct, scoreData);
|
||
|
||
// Hide input, show results
|
||
this.hideQuizSections();
|
||
document.getElementById('quiz-results').style.display = 'block';
|
||
}
|
||
|
||
calculateScore() {
|
||
const correct = [];
|
||
const correctSet = new Set(this.correctAnswers);
|
||
const answerSet = new Set(this.answers);
|
||
|
||
// Find correct answers
|
||
this.answers.forEach(answer => {
|
||
if (correctSet.has(answer)) {
|
||
correct.push(answer);
|
||
}
|
||
});
|
||
|
||
// Calculate base score as percentage of correct answers
|
||
const baseScore = (correct.length / this.correctAnswers.length) * 100;
|
||
|
||
// Calculate penalty: lose 5 points per incorrect guess, minimum 0%
|
||
const penalty = this.incorrectGuesses * 5;
|
||
const finalScore = Math.max(0, baseScore - penalty);
|
||
|
||
return {
|
||
correct: correct,
|
||
baseScore: baseScore,
|
||
penalty: penalty,
|
||
incorrectGuesses: this.incorrectGuesses,
|
||
finalScore: finalScore
|
||
};
|
||
}
|
||
|
||
showDetailedResults(correct, scoreData) {
|
||
const resultsEl = document.getElementById('results-list');
|
||
const correctSet = new Set(correct);
|
||
const answerSet = new Set(this.answers);
|
||
|
||
let html = '';
|
||
|
||
// Show scoring breakdown if there were penalties
|
||
if (scoreData && scoreData.incorrectGuesses > 0) {
|
||
html += `<div class="result-item score-breakdown">
|
||
<div style="margin-bottom: 10px; font-weight: bold; color: #2c5f76;">Score Breakdown:</div>
|
||
<div style="font-size: 0.9em; color: #666;">
|
||
Base Score: ${Math.round(scoreData.baseScore)}% (${correct.length} of ${this.correctAnswers.length} correct)<br>
|
||
Penalty: -${scoreData.penalty}% (${scoreData.incorrectGuesses} wrong guess${scoreData.incorrectGuesses > 1 ? 'es' : ''} × 5 points each)<br>
|
||
<strong style="color: #2c5f76;">Final Score: ${Math.round(scoreData.finalScore)}%</strong>
|
||
</div>
|
||
</div>`;
|
||
}
|
||
|
||
// Show all correct answers and whether user got them
|
||
this.correctAnswers.forEach(num => {
|
||
const wasCorrect = correctSet.has(num);
|
||
const className = wasCorrect ? 'result-correct' : 'result-incorrect';
|
||
const status = wasCorrect ? '✓ Correct' : '✗ Missed';
|
||
html += `<div class="result-item">
|
||
<span>Card: ${num}</span>
|
||
<span class="${className}">${status}</span>
|
||
</div>`;
|
||
});
|
||
|
||
// Show any extra incorrect answers
|
||
const extraAnswers = this.answers.filter(a => !correctSet.has(a) && this.correctAnswers.includes(a) === false);
|
||
extraAnswers.forEach(num => {
|
||
html += `<div class="result-item">
|
||
<span>Wrong guess: ${num}</span>
|
||
<span class="result-incorrect">✗ Not in quiz (-5 points)</span>
|
||
</div>`;
|
||
});
|
||
|
||
resultsEl.innerHTML = html;
|
||
}
|
||
|
||
endQuiz() {
|
||
// Stop the current quiz and return to configuration
|
||
this.cleanupQuizState();
|
||
this.resetQuiz();
|
||
}
|
||
|
||
resetQuiz() {
|
||
// Reset state
|
||
this.currentCardIndex = 0;
|
||
this.answers = [];
|
||
this.correctAnswers = [];
|
||
this.quizCards = [];
|
||
this.foundNumbers = [];
|
||
this.guessesRemaining = 0;
|
||
this.currentInput = '';
|
||
this.incorrectGuesses = 0;
|
||
this.finishButtonsBound = false;
|
||
|
||
// Clean up any pending timeouts
|
||
this.cleanupQuizState();
|
||
|
||
}
|
||
|
||
cleanupQuizState() {
|
||
// Clear any pending prefix acceptance timeout
|
||
if (this.prefixAcceptanceTimeout) {
|
||
clearTimeout(this.prefixAcceptanceTimeout);
|
||
this.prefixAcceptanceTimeout = null;
|
||
}
|
||
|
||
// Clear smart input
|
||
const smartInput = document.getElementById('smart-input');
|
||
if (smartInput) {
|
||
smartInput.value = '';
|
||
smartInput.classList.remove('correct', 'incorrect');
|
||
}
|
||
|
||
// Clear found numbers display
|
||
const foundContainer = document.getElementById('found-numbers');
|
||
if (foundContainer) {
|
||
foundContainer.innerHTML = '';
|
||
}
|
||
|
||
// Reset stats display
|
||
document.getElementById('cards-shown-count').textContent = '0';
|
||
document.getElementById('guesses-remaining').textContent = '0';
|
||
document.getElementById('numbers-found').textContent = '0';
|
||
|
||
// Hide finish buttons
|
||
document.getElementById('finish-quiz').style.display = 'none';
|
||
document.getElementById('give-up-quiz').style.display = 'none';
|
||
|
||
// Reset to initial quiz state (hide all sections, show controls)
|
||
this.hideQuizSections();
|
||
document.querySelector('.quiz-controls').style.display = 'block';
|
||
}
|
||
|
||
hideQuizSections() {
|
||
document.getElementById('quiz-game').style.display = 'none';
|
||
document.getElementById('quiz-input').style.display = 'none';
|
||
document.getElementById('quiz-results').style.display = 'none';
|
||
}
|
||
|
||
delay(ms) {
|
||
return new Promise(resolve => setTimeout(resolve, ms));
|
||
}
|
||
}
|
||
|
||
// Card Sorting Challenge - Drag and drop functionality
|
||
class SortingChallenge {
|
||
constructor() {
|
||
this.cards = [];
|
||
this.sortingCards = [];
|
||
this.selectedCount = 5;
|
||
this.currentOrder = [];
|
||
this.correctOrder = [];
|
||
this.isDragging = false;
|
||
this.draggedElement = null;
|
||
this.draggedIndex = -1;
|
||
this.previewOrder = [];
|
||
this.lastInsertIndex = -1;
|
||
this.touchStartElement = null;
|
||
this.touchStartIndex = -1;
|
||
this.touchStartX = 0;
|
||
this.touchStartY = 0;
|
||
|
||
this.initializeSorting();
|
||
this.bindSortingEvents();
|
||
}
|
||
|
||
initializeSorting() {
|
||
// Get available cards (same as quiz cards)
|
||
const cardElements = document.querySelectorAll('.flashcard');
|
||
this.cards = Array.from(cardElements).map(card => ({
|
||
number: parseInt(card.dataset.number),
|
||
svg: card.querySelector('.abacus-container').outerHTML
|
||
}));
|
||
}
|
||
|
||
bindSortingEvents() {
|
||
// Card count selection
|
||
document.querySelectorAll('.sort-count-btn').forEach(btn => {
|
||
btn.addEventListener('click', (e) => {
|
||
document.querySelectorAll('.sort-count-btn').forEach(b => b.classList.remove('active'));
|
||
e.target.classList.add('active');
|
||
this.selectedCount = parseInt(e.target.dataset.count);
|
||
});
|
||
});
|
||
|
||
// Action buttons
|
||
document.getElementById('start-sorting').addEventListener('click', () => this.startSorting());
|
||
document.getElementById('check-sorting').addEventListener('click', () => this.checkSolution());
|
||
document.getElementById('reveal-numbers').addEventListener('click', () => this.revealNumbers());
|
||
document.getElementById('new-sorting').addEventListener('click', () => this.newChallenge());
|
||
document.getElementById('end-sorting').addEventListener('click', () => this.endSorting());
|
||
|
||
// Score modal event listeners
|
||
document.querySelector('.score-modal-close').addEventListener('click', () => this.hideScoreModal());
|
||
document.getElementById('score-modal-close-btn').addEventListener('click', () => this.hideScoreModal());
|
||
document.getElementById('score-modal-new-game').addEventListener('click', () => {
|
||
this.hideScoreModal();
|
||
this.newChallenge();
|
||
});
|
||
|
||
// Close modal when clicking outside
|
||
document.getElementById('score-modal').addEventListener('click', (e) => {
|
||
if (e.target.id === 'score-modal') {
|
||
this.hideScoreModal();
|
||
}
|
||
});
|
||
}
|
||
|
||
startSorting() {
|
||
// Select random cards for sorting
|
||
const shuffledCards = [...this.cards].sort(() => Math.random() - 0.5);
|
||
this.sortingCards = shuffledCards.slice(0, this.selectedCount);
|
||
this.correctOrder = [...this.sortingCards].sort((a, b) => a.number - b.number);
|
||
|
||
// Shuffle the display order
|
||
this.currentOrder = [...this.sortingCards].sort(() => Math.random() - 0.5);
|
||
|
||
// Initialize revealed state
|
||
this.numbersRevealed = false;
|
||
|
||
// Hide configuration controls, show only game
|
||
document.querySelector('.sorting-controls').style.display = 'none';
|
||
|
||
// Show sorting game and sticky header
|
||
document.getElementById('sorting-game').style.display = 'block';
|
||
document.getElementById('sorting-header').style.display = 'block';
|
||
|
||
this.renderSortingCards();
|
||
this.updateSortingStatus(`Arrange the ${this.selectedCount} cards in ascending order (smallest to largest)`);
|
||
|
||
// Start timer
|
||
this.startTimer();
|
||
|
||
// Reset reveal numbers button for new game
|
||
document.getElementById('reveal-numbers').style.display = 'inline-block';
|
||
|
||
// Update buttons - hide old controls, sticky header is now visible
|
||
document.getElementById('start-sorting').style.display = 'none';
|
||
document.querySelector('.sorting-game-actions').style.display = 'none';
|
||
}
|
||
|
||
getSequenceStyle(position, totalSlots) {
|
||
// Generate neutral warm gray gradient that won't clash with abacus colors
|
||
// Position 0 (first) = darkest, last position = lightest
|
||
const intensity = position / (totalSlots - 1); // 0 to 1
|
||
const lightness = 30 + (intensity * 45); // 30% to 75% lightness for better contrast
|
||
return {
|
||
background: `hsl(220, 8%, ${lightness}%)`, // Very subtle blue-gray, low saturation
|
||
color: lightness > 60 ? '#2c3e50' : '#ffffff', // High contrast text
|
||
borderColor: lightness > 60 ? '#2c5f76' : 'rgba(255,255,255,0.4)'
|
||
};
|
||
}
|
||
|
||
renderSortingCards() {
|
||
this.createPositionSlots();
|
||
this.renderAvailableCards();
|
||
}
|
||
|
||
createPositionSlots() {
|
||
const slotsContainer = document.getElementById('position-slots');
|
||
if (!slotsContainer) return;
|
||
|
||
slotsContainer.innerHTML = '';
|
||
this.placedCards = new Array(this.selectedCount).fill(null);
|
||
|
||
// Add insert button before the very first position
|
||
const firstInsertBtn = document.createElement('button');
|
||
firstInsertBtn.className = 'insert-button';
|
||
firstInsertBtn.innerHTML = '+';
|
||
firstInsertBtn.dataset.insertAt = 0;
|
||
firstInsertBtn.addEventListener('click', (e) => this.handleInsertClick(0));
|
||
slotsContainer.appendChild(firstInsertBtn);
|
||
|
||
for (let i = 0; i < this.selectedCount; i++) {
|
||
// Add the position slot
|
||
const slot = document.createElement('div');
|
||
slot.className = 'position-slot';
|
||
slot.dataset.position = i;
|
||
|
||
// Apply gradient to entire slot background
|
||
const style = this.getSequenceStyle(i, this.selectedCount);
|
||
slot.style.background = style.background;
|
||
slot.style.color = style.color;
|
||
slot.style.borderColor = style.borderColor;
|
||
|
||
slot.innerHTML = `
|
||
<div class="slot-label" style="color: ${style.color}">${i === 0 ? 'Smallest' : i === this.selectedCount - 1 ? 'Largest' : ''}</div>
|
||
`;
|
||
|
||
slot.addEventListener('click', (e) => this.handleSlotClick(i));
|
||
slotsContainer.appendChild(slot);
|
||
|
||
// Add insert button after each position
|
||
const insertBtn = document.createElement('button');
|
||
insertBtn.className = 'insert-button';
|
||
insertBtn.innerHTML = '+';
|
||
insertBtn.dataset.insertAt = i + 1;
|
||
insertBtn.addEventListener('click', (e) => this.handleInsertClick(i + 1));
|
||
slotsContainer.appendChild(insertBtn);
|
||
}
|
||
}
|
||
|
||
renderAvailableCards() {
|
||
const sortingArea = document.getElementById('sorting-area');
|
||
if (!sortingArea) return;
|
||
|
||
sortingArea.innerHTML = '';
|
||
|
||
// Remove duplicates and filter out placed cards
|
||
const uniqueAvailable = this.currentOrder.filter((card, index, arr) => {
|
||
// Only keep first occurrence of each card number
|
||
const firstIndex = arr.findIndex(c => c.number === card.number);
|
||
if (firstIndex !== index) {
|
||
console.warn(`Duplicate card found: ${card.number}, removing duplicate`);
|
||
return false;
|
||
}
|
||
|
||
// Skip if already placed
|
||
if (this.placedCards.some(placed => placed && placed.number === card.number)) {
|
||
console.warn(`Card ${card.number} is both available and placed, removing from available`);
|
||
return false;
|
||
}
|
||
|
||
return true;
|
||
});
|
||
|
||
// Update currentOrder to clean version
|
||
this.currentOrder = uniqueAvailable;
|
||
|
||
this.currentOrder.forEach((card, index) => {
|
||
const cardEl = document.createElement('div');
|
||
cardEl.className = 'sort-card';
|
||
cardEl.dataset.number = card.number;
|
||
|
||
// Apply revealed state if numbers were previously revealed
|
||
if (this.numbersRevealed) {
|
||
cardEl.classList.add('revealed');
|
||
}
|
||
|
||
cardEl.innerHTML = `
|
||
<div class="revealed-number">${card.number}</div>
|
||
<div class="card-svg">${card.svg}</div>
|
||
`;
|
||
|
||
cardEl.addEventListener('click', (e) => this.handleCardClick(card, cardEl));
|
||
sortingArea.appendChild(cardEl);
|
||
});
|
||
}
|
||
|
||
handleCardClick(card, cardElement) {
|
||
// Clear any previously selected cards
|
||
document.querySelectorAll('.sort-card.selected').forEach(el => {
|
||
el.classList.remove('selected');
|
||
});
|
||
|
||
// Select this card
|
||
cardElement.classList.add('selected');
|
||
this.selectedCard = card;
|
||
this.selectedCardElement = cardElement;
|
||
|
||
// Highlight available positions and insert buttons
|
||
document.querySelectorAll('.position-slot').forEach(slot => {
|
||
if (!slot.classList.contains('filled')) {
|
||
slot.classList.add('active');
|
||
}
|
||
});
|
||
|
||
document.querySelectorAll('.insert-button').forEach(btn => {
|
||
btn.classList.add('active');
|
||
});
|
||
|
||
this.updateSortingStatus(`Selected card with value ${card.number}. Click a position or + button to place it.`);
|
||
}
|
||
|
||
handleInsertClick(insertPosition) {
|
||
if (!this.selectedCard) {
|
||
this.updateSortingStatus('Please select a card first, then click where to insert it.');
|
||
return;
|
||
}
|
||
|
||
// Handle insertion at the rightmost position (beyond current array bounds)
|
||
if (insertPosition >= this.selectedCount) {
|
||
// Find the rightmost empty position
|
||
let rightmostEmptyPos = -1;
|
||
for (let i = this.selectedCount - 1; i >= 0; i--) {
|
||
if (this.placedCards[i] === null) {
|
||
rightmostEmptyPos = i;
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (rightmostEmptyPos === -1) {
|
||
this.updateSortingStatus('All positions are filled. Move a card first to make room.');
|
||
return;
|
||
}
|
||
|
||
// Place card in the rightmost empty position
|
||
this.placedCards[rightmostEmptyPos] = this.selectedCard;
|
||
} else {
|
||
// Create a new array for placed cards
|
||
const newPlacedCards = new Array(this.selectedCount).fill(null);
|
||
|
||
// Copy existing cards, shifting them as needed
|
||
for (let i = 0; i < this.placedCards.length; i++) {
|
||
if (this.placedCards[i] !== null) {
|
||
if (i < insertPosition) {
|
||
// Cards before insert position stay in same place
|
||
newPlacedCards[i] = this.placedCards[i];
|
||
} else {
|
||
// Cards at or after insert position shift right by 1
|
||
if (i + 1 < this.selectedCount) {
|
||
newPlacedCards[i + 1] = this.placedCards[i];
|
||
} else {
|
||
// Card would fall off - store temporarily, will handle below
|
||
newPlacedCards[i + 1] = this.placedCards[i];
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// Place the selected card at the insert position
|
||
newPlacedCards[insertPosition] = this.selectedCard;
|
||
|
||
// Now apply gap-filling logic: shift cards left to compress and eliminate gaps
|
||
const compactedCards = [];
|
||
for (let i = 0; i < newPlacedCards.length; i++) {
|
||
if (newPlacedCards[i] !== null) {
|
||
compactedCards.push(newPlacedCards[i]);
|
||
}
|
||
}
|
||
|
||
// If we have more cards than positions, put excess back in available
|
||
if (compactedCards.length > this.selectedCount) {
|
||
const excessCards = compactedCards.slice(this.selectedCount);
|
||
this.currentOrder.push(...excessCards);
|
||
compactedCards.splice(this.selectedCount);
|
||
}
|
||
|
||
// Fill the final array with compacted cards (no gaps)
|
||
const finalPlacedCards = new Array(this.selectedCount).fill(null);
|
||
for (let i = 0; i < compactedCards.length; i++) {
|
||
finalPlacedCards[i] = compactedCards[i];
|
||
}
|
||
|
||
this.placedCards = finalPlacedCards;
|
||
}
|
||
|
||
// Remove card from available cards
|
||
this.currentOrder = this.currentOrder.filter(c => c.number !== this.selectedCard.number);
|
||
|
||
// Debug: Check total card count
|
||
this.debugCardCount('after insert');
|
||
|
||
// Clear selection and re-render
|
||
this.clearSelection();
|
||
this.updatePositionSlots();
|
||
this.renderAvailableCards();
|
||
|
||
// Update status
|
||
const placedCount = this.placedCards.filter(c => c !== null).length;
|
||
if (placedCount === this.selectedCount) {
|
||
this.updateSortingStatus('All cards placed! Click "Check My Solution" to see how you did.');
|
||
} else {
|
||
this.updateSortingStatus(`${placedCount}/${this.selectedCount} cards placed. Select another card to continue.`);
|
||
}
|
||
}
|
||
|
||
debugCardCount(context) {
|
||
const placedCount = this.placedCards.filter(c => c !== null).length;
|
||
const availableCount = this.currentOrder.length;
|
||
const totalCount = placedCount + availableCount;
|
||
|
||
console.log(`DEBUG ${context}: Placed=${placedCount}, Available=${availableCount}, Total=${totalCount}, Expected=${this.selectedCount}`);
|
||
|
||
if (totalCount !== this.selectedCount) {
|
||
console.error('Card count mismatch! Some cards are missing or duplicated.');
|
||
console.log('Placed cards:', this.placedCards.map(c => c ? c.number : 'empty'));
|
||
console.log('Available cards:', this.currentOrder.map(c => c.number));
|
||
}
|
||
}
|
||
|
||
updatePositionSlots() {
|
||
this.placedCards.forEach((card, position) => {
|
||
const slot = document.querySelector(`[data-position="${position}"]`);
|
||
if (!slot) return;
|
||
|
||
if (card) {
|
||
slot.classList.add('filled');
|
||
// Reset to white background when filled
|
||
slot.style.background = '#fff';
|
||
slot.style.color = '#333';
|
||
slot.style.borderColor = '#2c5f76';
|
||
slot.innerHTML = `
|
||
<div class="slot-card">
|
||
<div class="card-svg">${card.svg}</div>
|
||
</div>
|
||
<div class="slot-label">← Click to move back</div>
|
||
`;
|
||
} else {
|
||
slot.classList.remove('filled');
|
||
// Apply gradient to empty slot
|
||
const style = this.getSequenceStyle(position, this.selectedCount);
|
||
slot.style.background = style.background;
|
||
slot.style.color = style.color;
|
||
slot.style.borderColor = style.borderColor;
|
||
slot.innerHTML = `
|
||
<div class="slot-label" style="color: ${style.color}">${position === 0 ? 'Smallest' : position === this.selectedCount - 1 ? 'Largest' : ''}</div>
|
||
`;
|
||
}
|
||
});
|
||
}
|
||
|
||
clearSelection() {
|
||
this.selectedCard = null;
|
||
this.selectedCardElement = null;
|
||
|
||
// Remove active states
|
||
document.querySelectorAll('.sort-card.selected').forEach(el => {
|
||
el.classList.remove('selected');
|
||
});
|
||
document.querySelectorAll('.position-slot.active').forEach(el => {
|
||
el.classList.remove('active');
|
||
});
|
||
document.querySelectorAll('.insert-button.active').forEach(el => {
|
||
el.classList.remove('active');
|
||
});
|
||
}
|
||
|
||
handleSlotClick(position) {
|
||
const slot = document.querySelector(`[data-position="${position}"]`);
|
||
|
||
// If no card is selected but slot has a card, move it back to available
|
||
if (!this.selectedCard && this.placedCards[position]) {
|
||
const cardToMove = this.placedCards[position];
|
||
|
||
// Remove card from this position
|
||
this.placedCards[position] = null;
|
||
|
||
// Add card back to available cards
|
||
this.currentOrder.push(cardToMove);
|
||
|
||
// Debug: Check total card count
|
||
this.debugCardCount('after moving card back');
|
||
|
||
// Update slot appearance
|
||
this.updatePositionSlots();
|
||
this.renderAvailableCards();
|
||
|
||
// Auto-select the moved card so user can immediately place it elsewhere
|
||
this.selectedCard = cardToMove;
|
||
this.selectedCardElement = document.querySelector(`[data-number="${cardToMove.number}"]`);
|
||
if (this.selectedCardElement) {
|
||
this.selectedCardElement.classList.add('selected');
|
||
|
||
// Highlight available positions
|
||
document.querySelectorAll('.position-slot').forEach(slot => {
|
||
if (!slot.classList.contains('filled')) {
|
||
slot.classList.add('active');
|
||
}
|
||
});
|
||
document.querySelectorAll('.insert-button').forEach(btn => {
|
||
btn.classList.add('active');
|
||
btn.classList.remove('disabled');
|
||
});
|
||
}
|
||
|
||
const placedCount = this.placedCards.filter(c => c !== null).length;
|
||
this.updateSortingStatus(`Moved card ${cardToMove.number} back and selected it. ${placedCount}/${this.selectedCount} cards placed.`);
|
||
return;
|
||
}
|
||
|
||
if (!this.selectedCard) {
|
||
this.updateSortingStatus('Select a card first, or click a placed card to move it back.');
|
||
return;
|
||
}
|
||
|
||
// If slot is already filled, replace the card
|
||
if (this.placedCards[position]) {
|
||
// Move the previous card back to available area
|
||
this.currentOrder.push(this.placedCards[position]);
|
||
}
|
||
|
||
// Place the selected card in this position
|
||
this.placedCards[position] = this.selectedCard;
|
||
|
||
// Remove card from current order (available cards)
|
||
this.currentOrder = this.currentOrder.filter(c => c.number !== this.selectedCard.number);
|
||
|
||
// Debug: Check total card count
|
||
this.debugCardCount('after regular placement');
|
||
|
||
// Update slot appearance
|
||
slot.classList.add('filled');
|
||
// Reset to white background when filled
|
||
slot.style.background = '#fff';
|
||
slot.style.color = '#333';
|
||
slot.style.borderColor = '#2c5f76';
|
||
slot.innerHTML = `
|
||
<div class="slot-card">
|
||
<div class="card-svg">${this.selectedCard.svg}</div>
|
||
</div>
|
||
<div class="slot-label">← Click to move back</div>
|
||
`;
|
||
|
||
// Clear selection and re-render
|
||
this.clearSelection();
|
||
this.renderAvailableCards();
|
||
|
||
// Update status
|
||
const placedCount = this.placedCards.filter(c => c !== null).length;
|
||
if (placedCount === this.selectedCount) {
|
||
this.updateSortingStatus('All cards placed! Click "Check My Solution" to see how you did.');
|
||
} else {
|
||
this.updateSortingStatus(`${placedCount}/${this.selectedCount} cards placed. Select another card to continue.`);
|
||
}
|
||
}
|
||
|
||
handleDragEnd(e) {
|
||
if (this.previewOrder.length > 0) {
|
||
// Commit the preview order to the actual order
|
||
this.currentOrder = [...this.previewOrder];
|
||
}
|
||
|
||
// Clean up
|
||
this.resetDragState();
|
||
|
||
// Re-render with final positions
|
||
this.renderSortingCards();
|
||
|
||
// Add success animation
|
||
setTimeout(() => {
|
||
document.querySelectorAll('.sort-card').forEach(card => {
|
||
card.classList.add('success');
|
||
setTimeout(() => card.classList.remove('success'), 600);
|
||
});
|
||
}, 100);
|
||
}
|
||
|
||
handleDragOver(e) {
|
||
if (e.preventDefault) e.preventDefault();
|
||
e.dataTransfer.dropEffect = 'move';
|
||
|
||
// Calculate where to insert based on mouse position
|
||
const insertIndex = this.calculateInsertIndex(e.clientX, e.clientY);
|
||
console.log('DragOver - insertIndex:', insertIndex, 'lastInsertIndex:', this.lastInsertIndex);
|
||
if (insertIndex !== -1 && insertIndex !== this.lastInsertIndex) {
|
||
console.log('Calling updatePreviewOrder with insertIndex:', insertIndex);
|
||
this.updatePreviewOrder(insertIndex);
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
handleDragEnter(e) {
|
||
// Handle drag enter for the sorting area
|
||
}
|
||
|
||
handleDragLeave(e) {
|
||
// Handle drag leave
|
||
}
|
||
|
||
handleDrop(e) {
|
||
if (e.stopPropagation) e.stopPropagation();
|
||
// The preview order is already set, just let dragEnd handle the commit
|
||
return false;
|
||
}
|
||
|
||
calculateInsertIndex(clientX, clientY) {
|
||
const sortingArea = document.getElementById('sorting-area');
|
||
const cards = Array.from(sortingArea.querySelectorAll('.sort-card:not(.dragging)'));
|
||
|
||
if (cards.length === 0) return 0;
|
||
|
||
|
||
// Simple approach: find which card the mouse is over
|
||
for (let i = 0; i < cards.length; i++) {
|
||
const card = cards[i];
|
||
const rect = card.getBoundingClientRect();
|
||
|
||
// Check if mouse is over this card
|
||
if (clientX >= rect.left && clientX <= rect.right &&
|
||
clientY >= rect.top && clientY <= rect.bottom) {
|
||
|
||
const cardIndex = parseInt(card.dataset.index);
|
||
const cardCenterX = rect.left + rect.width / 2;
|
||
|
||
const insertIndex = clientX < cardCenterX ? cardIndex : cardIndex + 1;
|
||
return insertIndex;
|
||
}
|
||
}
|
||
|
||
return this.currentOrder.length;
|
||
}
|
||
|
||
updatePreviewOrder(insertIndex) {
|
||
if (this.lastInsertIndex === insertIndex) return; // No change needed
|
||
this.lastInsertIndex = insertIndex;
|
||
|
||
console.log('updatePreviewOrder called with insertIndex:', insertIndex);
|
||
console.log('currentOrder:', this.currentOrder.map(c => c.number));
|
||
console.log('draggedIndex:', this.draggedIndex);
|
||
|
||
// Create new preview order
|
||
const newOrder = [...this.currentOrder];
|
||
const draggedCard = newOrder.splice(this.draggedIndex, 1)[0];
|
||
|
||
// Adjust insert index if we removed an item before it
|
||
let adjustedInsertIndex = insertIndex;
|
||
if (this.draggedIndex < insertIndex) {
|
||
adjustedInsertIndex--;
|
||
}
|
||
|
||
// Insert at new position
|
||
newOrder.splice(adjustedInsertIndex, 0, draggedCard);
|
||
this.previewOrder = newOrder;
|
||
|
||
console.log('New preview order:', this.previewOrder.map(c => c.number));
|
||
|
||
// Update the visual layout immediately
|
||
this.renderPreview();
|
||
}
|
||
|
||
renderPreview() {
|
||
console.log('renderPreview called');
|
||
const sortingArea = document.getElementById('sorting-area');
|
||
|
||
// Get all cards except the dragged one
|
||
const cards = Array.from(sortingArea.querySelectorAll('.sort-card:not(.dragging)'));
|
||
console.log('Found cards (excluding dragged):', cards.length);
|
||
|
||
const draggedNumber = parseInt(this.draggedElement.dataset.number);
|
||
|
||
// Simply compare the full orders - if they're different, reorder
|
||
const previewNumbers = this.previewOrder.map(c => c.number);
|
||
const currentNumbers = this.currentOrder.map(c => c.number);
|
||
|
||
console.log('Preview order:', previewNumbers);
|
||
console.log('Current order:', currentNumbers);
|
||
|
||
// Check if the orders are different
|
||
const needsReorder = !previewNumbers.every((num, index) => currentNumbers[index] === num);
|
||
|
||
console.log('Needs reorder:', needsReorder);
|
||
|
||
if (needsReorder) {
|
||
console.log('Performing reorder...');
|
||
|
||
// Update currentOrder to match preview (this is key!)
|
||
this.currentOrder = [...this.previewOrder];
|
||
|
||
// Create a simple reordering without removing cards from DOM
|
||
// Just change their visual order using CSS flexbox order or reinsert in correct order
|
||
|
||
const previewWithoutDragged = this.previewOrder.filter(card => card.number !== draggedNumber);
|
||
console.log('Cards to reorder:', previewWithoutDragged.map(c => c.number));
|
||
console.log('Available DOM cards:', cards.map(c => parseInt(c.dataset.number)));
|
||
|
||
// Store references to all current cards
|
||
const cardElements = new Map();
|
||
cards.forEach(card => {
|
||
cardElements.set(parseInt(card.dataset.number), card);
|
||
});
|
||
|
||
// Reorder by moving each card to the correct position
|
||
previewWithoutDragged.forEach((card, targetIndex) => {
|
||
const cardEl = cardElements.get(card.number);
|
||
console.log('Finding card', card.number, 'found element:', !!cardEl);
|
||
if (cardEl) {
|
||
cardEl.dataset.index = targetIndex;
|
||
cardEl.querySelector('.card-position').textContent = targetIndex + 1;
|
||
|
||
// Move to correct position in DOM
|
||
const currentIndex = Array.from(sortingArea.children).indexOf(cardEl);
|
||
const targetPosition = targetIndex;
|
||
|
||
if (currentIndex !== targetPosition) {
|
||
if (targetPosition >= sortingArea.children.length - 1) {
|
||
sortingArea.appendChild(cardEl);
|
||
} else {
|
||
const nextSibling = sortingArea.children[targetPosition];
|
||
sortingArea.insertBefore(cardEl, nextSibling);
|
||
}
|
||
}
|
||
}
|
||
});
|
||
|
||
// Ensure dragged element stays at the end for z-index
|
||
if (this.draggedElement && this.draggedElement.parentNode === sortingArea) {
|
||
sortingArea.appendChild(this.draggedElement);
|
||
}
|
||
}
|
||
}
|
||
|
||
resetDragState() {
|
||
if (this.draggedElement) {
|
||
this.draggedElement.classList.remove('dragging');
|
||
this.draggedElement.style.transform = '';
|
||
this.draggedElement.style.position = '';
|
||
this.draggedElement.style.pointerEvents = '';
|
||
}
|
||
this.isDragging = false;
|
||
this.draggedElement = null;
|
||
this.draggedIndex = -1;
|
||
this.previewOrder = [];
|
||
this.lastInsertIndex = -1;
|
||
}
|
||
|
||
// Enhanced touch support for mobile with real-time reordering
|
||
handleTouchStart(e) {
|
||
const touch = e.touches[0];
|
||
this.touchStartX = touch.clientX;
|
||
this.touchStartY = touch.clientY;
|
||
this.touchStartElement = e.target.closest('.sort-card');
|
||
this.touchStartIndex = this.touchStartElement ? parseInt(this.touchStartElement.dataset.index) : -1;
|
||
|
||
if (this.touchStartElement) {
|
||
this.isDragging = true;
|
||
this.draggedElement = this.touchStartElement;
|
||
this.draggedIndex = this.touchStartIndex;
|
||
this.previewOrder = [...this.currentOrder];
|
||
this.lastInsertIndex = -1;
|
||
|
||
this.touchStartElement.classList.add('dragging');
|
||
this.createPlaceholder();
|
||
}
|
||
}
|
||
|
||
handleTouchMove(e) {
|
||
e.preventDefault();
|
||
|
||
if (this.touchStartElement && this.isDragging) {
|
||
const touch = e.touches[0];
|
||
|
||
// Update dragged element position
|
||
const deltaX = touch.clientX - this.touchStartX;
|
||
const deltaY = touch.clientY - this.touchStartY;
|
||
this.touchStartElement.style.transform = `translate(${deltaX}px, ${deltaY}px) rotate(5deg) scale(1.05)`;
|
||
|
||
// Calculate insert position and update preview
|
||
const insertIndex = this.calculateInsertIndex(touch.clientX, touch.clientY);
|
||
if (insertIndex !== -1) {
|
||
this.updatePreviewOrder(insertIndex);
|
||
}
|
||
}
|
||
}
|
||
|
||
handleTouchEnd(e) {
|
||
if (!this.touchStartElement || !this.isDragging) return;
|
||
|
||
// Commit the preview order
|
||
this.currentOrder = [...this.previewOrder];
|
||
|
||
// Clean up
|
||
this.touchStartElement.classList.remove('dragging');
|
||
this.touchStartElement.style.transform = '';
|
||
this.removePlaceholder();
|
||
|
||
this.isDragging = false;
|
||
this.draggedElement = null;
|
||
this.draggedIndex = -1;
|
||
this.previewOrder = [];
|
||
this.lastInsertIndex = -1;
|
||
|
||
// Re-render final positions
|
||
this.renderSortingCards();
|
||
|
||
// Success animation
|
||
setTimeout(() => {
|
||
document.querySelectorAll('.sort-card').forEach(card => {
|
||
card.classList.add('success');
|
||
setTimeout(() => card.classList.remove('success'), 600);
|
||
});
|
||
}, 100);
|
||
|
||
this.touchStartElement = null;
|
||
this.touchStartIndex = -1;
|
||
}
|
||
|
||
checkSolution() {
|
||
if (this.placedCards.some(card => card === null)) {
|
||
this.updateSortingStatus('Please place all cards before checking your solution.');
|
||
return;
|
||
}
|
||
|
||
// Get the sequences for comparison
|
||
const userSequence = this.placedCards.map(card => card.number);
|
||
const correctSequence = this.correctOrder.map(card => card.number);
|
||
|
||
// Calculate fair score using sequence alignment
|
||
const scoreResult = this.calculateSequenceScore(userSequence, correctSequence);
|
||
|
||
// Update visual feedback for each position
|
||
this.placedCards.forEach((card, index) => {
|
||
const slot = document.querySelector(`[data-position="${index}"]`);
|
||
const isExactMatch = card.number === this.correctOrder[index].number;
|
||
|
||
if (isExactMatch) {
|
||
slot.classList.add('correct');
|
||
slot.classList.remove('incorrect');
|
||
} else {
|
||
slot.classList.add('incorrect');
|
||
slot.classList.remove('correct');
|
||
}
|
||
});
|
||
|
||
this.showAdvancedFeedback(userSequence, correctSequence);
|
||
}
|
||
|
||
calculateSequenceScore(userSeq, correctSeq) {
|
||
// Calculate Longest Common Subsequence (LCS) score
|
||
const lcsLength = this.longestCommonSubsequence(userSeq, correctSeq);
|
||
|
||
// Calculate how many cards are in correct relative order
|
||
const relativeOrderScore = (lcsLength / correctSeq.length) * 100;
|
||
|
||
// Calculate exact position matches
|
||
let exactMatches = 0;
|
||
for (let i = 0; i < userSeq.length; i++) {
|
||
if (userSeq[i] === correctSeq[i]) {
|
||
exactMatches++;
|
||
}
|
||
}
|
||
const exactScore = (exactMatches / correctSeq.length) * 100;
|
||
|
||
// Calculate inversion count (how "scrambled" the sequence is)
|
||
const inversions = this.countInversions(userSeq, correctSeq);
|
||
const maxInversions = (correctSeq.length * (correctSeq.length - 1)) / 2;
|
||
const inversionScore = Math.max(0, ((maxInversions - inversions) / maxInversions) * 100);
|
||
|
||
// Weighted final score:
|
||
// - 50% for having cards in correct relative order (LCS)
|
||
// - 30% for exact position matches
|
||
// - 20% for overall sequence organization (inversions)
|
||
const finalScore = Math.round(
|
||
(relativeOrderScore * 0.5) +
|
||
(exactScore * 0.3) +
|
||
(inversionScore * 0.2)
|
||
);
|
||
|
||
return {
|
||
percentage: finalScore,
|
||
exactMatches: exactMatches,
|
||
correctRelativeOrder: lcsLength,
|
||
totalCards: correctSeq.length,
|
||
details: {
|
||
relativeOrderScore: Math.round(relativeOrderScore),
|
||
exactScore: Math.round(exactScore),
|
||
inversionScore: Math.round(inversionScore),
|
||
inversions: inversions
|
||
}
|
||
};
|
||
}
|
||
|
||
longestCommonSubsequence(seq1, seq2) {
|
||
const m = seq1.length;
|
||
const n = seq2.length;
|
||
const dp = Array(m + 1).fill().map(() => Array(n + 1).fill(0));
|
||
|
||
for (let i = 1; i <= m; i++) {
|
||
for (let j = 1; j <= n; j++) {
|
||
if (seq1[i - 1] === seq2[j - 1]) {
|
||
dp[i][j] = dp[i - 1][j - 1] + 1;
|
||
} else {
|
||
dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
|
||
}
|
||
}
|
||
}
|
||
|
||
return dp[m][n];
|
||
}
|
||
|
||
countInversions(userSeq, correctSeq) {
|
||
// Create a mapping from value to correct position
|
||
const correctPositions = {};
|
||
correctSeq.forEach((val, idx) => {
|
||
correctPositions[val] = idx;
|
||
});
|
||
|
||
// Convert user sequence to "correct position" sequence
|
||
const userCorrectPositions = userSeq.map(val => correctPositions[val]);
|
||
|
||
// Count inversions in this position sequence
|
||
let inversions = 0;
|
||
for (let i = 0; i < userCorrectPositions.length; i++) {
|
||
for (let j = i + 1; j < userCorrectPositions.length; j++) {
|
||
if (userCorrectPositions[i] > userCorrectPositions[j]) {
|
||
inversions++;
|
||
}
|
||
}
|
||
}
|
||
|
||
return inversions;
|
||
}
|
||
|
||
showAdvancedFeedback(userSeq, correctSeq) {
|
||
const feedbackEl = document.getElementById('sorting-feedback');
|
||
|
||
// Calculate advanced scoring metrics
|
||
const lcsLength = this.longestCommonSubsequence(userSeq, correctSeq);
|
||
const relativeOrderScore = (lcsLength / correctSeq.length) * 100;
|
||
|
||
// Calculate exact position matches
|
||
let exactMatches = 0;
|
||
for (let i = 0; i < Math.min(userSeq.length, correctSeq.length); i++) {
|
||
if (userSeq[i] === correctSeq[i]) {
|
||
exactMatches++;
|
||
}
|
||
}
|
||
const exactScore = (exactMatches / correctSeq.length) * 100;
|
||
|
||
// Calculate inversion score
|
||
const inversions = this.countInversions(userSeq, correctSeq);
|
||
const maxInversions = (correctSeq.length * (correctSeq.length - 1)) / 2;
|
||
const inversionScore = Math.max(0, ((maxInversions - inversions) / maxInversions) * 100);
|
||
|
||
// Weighted final score
|
||
const finalScore = Math.round(
|
||
(relativeOrderScore * 0.5) +
|
||
(exactScore * 0.3) +
|
||
(inversionScore * 0.2)
|
||
);
|
||
|
||
const isPerfect = finalScore === 100;
|
||
|
||
let feedbackClass, message;
|
||
|
||
if (isPerfect) {
|
||
feedbackClass = 'feedback-perfect';
|
||
message = '🎉 Perfect! All cards in correct order!';
|
||
} else if (finalScore >= 80) {
|
||
feedbackClass = 'feedback-good';
|
||
message = '👍 Excellent! Very close to perfect!';
|
||
} else if (finalScore >= 60) {
|
||
feedbackClass = 'feedback-good';
|
||
message = '👍 Good job! You understand the pattern!';
|
||
} else {
|
||
feedbackClass = 'feedback-needs-work';
|
||
message = '💪 Keep practicing! Focus on reading each abacus carefully.';
|
||
}
|
||
|
||
// Show score in modal instead of inline
|
||
this.showScoreModal({
|
||
finalScore,
|
||
message,
|
||
relativeOrderScore,
|
||
exactScore,
|
||
inversionScore,
|
||
lcsLength,
|
||
correctSeq,
|
||
exactMatches,
|
||
feedbackClass,
|
||
elapsedTime: this.getElapsedTime()
|
||
});
|
||
|
||
// Keep inline feedback hidden
|
||
feedbackEl.style.display = 'none';
|
||
|
||
this.updateSortingStatus(isPerfect ? 'Perfect solution!' : `${finalScore}% score`);
|
||
}
|
||
|
||
revealNumbers() {
|
||
// Track revealed state
|
||
this.numbersRevealed = true;
|
||
|
||
document.querySelectorAll('.sort-card').forEach(card => {
|
||
card.classList.add('revealed');
|
||
});
|
||
|
||
document.getElementById('reveal-numbers').style.display = 'none';
|
||
this.updateSortingStatus('Numbers revealed - now you can see the correct order!');
|
||
}
|
||
|
||
startTimer() {
|
||
this.startTime = Date.now();
|
||
this.timerInterval = setInterval(() => {
|
||
const elapsed = Math.floor((Date.now() - this.startTime) / 1000);
|
||
const minutes = Math.floor(elapsed / 60);
|
||
const seconds = elapsed % 60;
|
||
document.getElementById('sorting-timer').textContent =
|
||
`${minutes}:${seconds.toString().padStart(2, '0')}`;
|
||
}, 1000);
|
||
}
|
||
|
||
stopTimer() {
|
||
if (this.timerInterval) {
|
||
clearInterval(this.timerInterval);
|
||
this.timerInterval = null;
|
||
}
|
||
}
|
||
|
||
getElapsedTime() {
|
||
if (this.startTime) {
|
||
return Math.floor((Date.now() - this.startTime) / 1000);
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
showScoreModal(scoreData) {
|
||
const {
|
||
finalScore, message, relativeOrderScore, exactScore, inversionScore,
|
||
lcsLength, correctSeq, exactMatches, feedbackClass, elapsedTime
|
||
} = scoreData;
|
||
|
||
const minutes = Math.floor(elapsedTime / 60);
|
||
const seconds = elapsedTime % 60;
|
||
const timeDisplay = `${minutes}:${seconds.toString().padStart(2, '0')}`;
|
||
|
||
document.getElementById('score-modal-body').innerHTML = `
|
||
<div class="feedback-score">${finalScore}%</div>
|
||
<div class="feedback-message">${message}</div>
|
||
<div class="feedback-time">⏱️ Time: ${timeDisplay}</div>
|
||
<div class="feedback-breakdown">
|
||
<div class="score-component">
|
||
<span class="component-label">Sequence Order:</span>
|
||
<span class="component-score">${Math.round(relativeOrderScore)}%</span>
|
||
<span class="component-weight">(50% weight)</span>
|
||
</div>
|
||
<div class="score-component">
|
||
<span class="component-label">Exact Positions:</span>
|
||
<span class="component-score">${Math.round(exactScore)}%</span>
|
||
<span class="component-weight">(30% weight)</span>
|
||
</div>
|
||
<div class="score-component">
|
||
<span class="component-label">Organization:</span>
|
||
<span class="component-score">${Math.round(inversionScore)}%</span>
|
||
<span class="component-weight">(20% weight)</span>
|
||
</div>
|
||
</div>
|
||
<div class="feedback-details">
|
||
Cards in correct order: ${lcsLength}/${correctSeq.length} •
|
||
Exact position matches: ${exactMatches}/${correctSeq.length}
|
||
</div>
|
||
`;
|
||
|
||
document.getElementById('score-modal').style.display = 'flex';
|
||
}
|
||
|
||
hideScoreModal() {
|
||
document.getElementById('score-modal').style.display = 'none';
|
||
}
|
||
|
||
endSorting() {
|
||
// End the current sorting and return to configuration
|
||
this.resetSorting();
|
||
}
|
||
|
||
resetSorting() {
|
||
// Reset state
|
||
this.currentOrder = [];
|
||
this.correctOrder = [];
|
||
this.placedCards = [];
|
||
this.selectedCard = null;
|
||
this.selectedCardElement = null;
|
||
|
||
// Clear feedback
|
||
document.getElementById('sorting-feedback').style.display = 'none';
|
||
|
||
// Hide game and sticky header, show configuration controls
|
||
document.getElementById('sorting-game').style.display = 'none';
|
||
document.getElementById('sorting-header').style.display = 'none';
|
||
document.querySelector('.sorting-controls').style.display = 'block';
|
||
|
||
// Stop timer
|
||
this.stopTimer();
|
||
|
||
// Reset buttons
|
||
document.getElementById('start-sorting').style.display = 'inline-block';
|
||
document.querySelector('.sorting-game-actions').style.display = 'none';
|
||
}
|
||
|
||
newChallenge() {
|
||
// Reset state but stay in game mode
|
||
this.currentOrder = [];
|
||
this.correctOrder = [];
|
||
this.placedCards = [];
|
||
this.selectedCard = null;
|
||
this.selectedCardElement = null;
|
||
this.numbersRevealed = false;
|
||
|
||
// Clear feedback
|
||
document.getElementById('sorting-feedback').style.display = 'none';
|
||
|
||
// Clear card states
|
||
document.querySelectorAll('.sort-card').forEach(card => {
|
||
card.classList.remove('correct', 'incorrect', 'revealed');
|
||
});
|
||
|
||
// Start a new challenge (keeping game mode active)
|
||
this.startSorting();
|
||
}
|
||
|
||
updateSortingStatus(message) {
|
||
document.getElementById('sorting-status').textContent = message;
|
||
}
|
||
}
|
||
|
||
// Matching Pairs Challenge
|
||
class MatchingChallenge {
|
||
constructor() {
|
||
this.cards = [];
|
||
this.gameCards = [];
|
||
this.flippedCards = [];
|
||
this.matchedPairs = 0;
|
||
this.totalPairs = 0;
|
||
this.moves = 0;
|
||
this.selectedPairs = 6;
|
||
this.gameStartTime = null;
|
||
this.timerInterval = null;
|
||
|
||
// Two-player mode variables
|
||
this.gameMode = 'single'; // 'single' or 'two-player'
|
||
this.gameType = 'abacus-numeral'; // 'abacus-numeral' or 'complement-pairs'
|
||
this.turnTimer = 0; // 0 = no timer
|
||
this.currentPlayer = 1;
|
||
this.player1Score = 0;
|
||
this.player2Score = 0;
|
||
this.eventsAlreadyBound = false;
|
||
this.turnTimeLeft = 0;
|
||
this.turnTimerInterval = null;
|
||
this.playerTurnActive = true;
|
||
this.isFirstMove = true;
|
||
|
||
this.initializeMatching();
|
||
this.bindMatchingEvents();
|
||
}
|
||
|
||
initializeMatching() {
|
||
// Get available cards (same as quiz cards)
|
||
const cardElements = document.querySelectorAll('.flashcard');
|
||
this.cards = Array.from(cardElements).map(card => ({
|
||
number: parseInt(card.dataset.number),
|
||
svg: card.querySelector('.abacus-container').outerHTML
|
||
}));
|
||
|
||
// Hide/show grid size buttons based on available cards
|
||
this.updateAvailableGridSizes();
|
||
}
|
||
|
||
updateAvailableGridSizes() {
|
||
const availableCards = this.cards.length;
|
||
const gridButtons = document.querySelectorAll('.match-size-btn');
|
||
|
||
gridButtons.forEach(button => {
|
||
const requiredPairs = parseInt(button.dataset.pairs);
|
||
if (availableCards < requiredPairs) {
|
||
button.style.display = 'none';
|
||
} else {
|
||
button.style.display = 'block';
|
||
}
|
||
});
|
||
|
||
// Add a message if very few options are available
|
||
const visibleButtons = Array.from(gridButtons).filter(btn => btn.style.display !== 'none');
|
||
if (visibleButtons.length === 0) {
|
||
const countButtons = document.querySelector('.count-buttons');
|
||
countButtons.innerHTML += '<p style="color: #666; font-style: italic; margin-top: 15px;">Not enough flashcards available for memory matching. Need at least 6 unique cards.</p>';
|
||
} else if (visibleButtons.length < gridButtons.length) {
|
||
const countButtons = document.querySelector('.count-buttons');
|
||
const existingMessage = countButtons.querySelector('.cards-info');
|
||
if (!existingMessage) {
|
||
countButtons.innerHTML += `<p class="cards-info" style="color: #666; font-size: 0.9em; margin-top: 10px;">${availableCards} flashcards available</p>`;
|
||
}
|
||
}
|
||
}
|
||
|
||
bindMatchingEvents() {
|
||
// Skip if events are already bound to prevent duplicates
|
||
if (this.eventsAlreadyBound) {
|
||
return;
|
||
}
|
||
|
||
// Use event delegation on the modal container for reliable event handling
|
||
const modalBody = document.querySelector('#matching-modal .modal-body');
|
||
|
||
modalBody.addEventListener('click', (e) => {
|
||
// Handle player mode buttons
|
||
if (e.target.closest('.mode-btn')) {
|
||
const btn = e.target.closest('.mode-btn');
|
||
document.querySelectorAll('.mode-btn').forEach(b => b.classList.remove('active'));
|
||
btn.classList.add('active');
|
||
this.gameMode = btn.dataset.mode;
|
||
|
||
// Show/hide timer controls for two-player mode
|
||
const timerControls = document.getElementById('timer-controls');
|
||
if (this.gameMode === 'two-player') {
|
||
timerControls.style.display = 'block';
|
||
} else {
|
||
timerControls.style.display = 'none';
|
||
}
|
||
|
||
this.updateDescription();
|
||
return;
|
||
}
|
||
|
||
// Handle game type buttons
|
||
if (e.target.closest('.game-type-btn')) {
|
||
const btn = e.target.closest('.game-type-btn');
|
||
document.querySelectorAll('.game-type-btn').forEach(b => b.classList.remove('active'));
|
||
btn.classList.add('active');
|
||
this.gameType = btn.dataset.gameType;
|
||
|
||
this.updateDescription();
|
||
return;
|
||
}
|
||
|
||
// Handle timer buttons
|
||
if (e.target.closest('.timer-btn')) {
|
||
const btn = e.target.closest('.timer-btn');
|
||
document.querySelectorAll('.timer-btn').forEach(b => b.classList.remove('active'));
|
||
btn.classList.add('active');
|
||
this.turnTimer = parseInt(btn.dataset.timer);
|
||
return;
|
||
}
|
||
|
||
// Handle start game buttons
|
||
if (e.target.closest('.start-game-btn')) {
|
||
const btn = e.target.closest('.start-game-btn');
|
||
this.selectedPairs = parseInt(btn.dataset.pairs);
|
||
this.startMatching();
|
||
return;
|
||
}
|
||
});
|
||
|
||
this.eventsAlreadyBound = true;
|
||
|
||
// Action buttons (not in modal, so handle separately)
|
||
document.getElementById('new-matching')?.addEventListener('click', () => this.newChallenge());
|
||
document.getElementById('end-matching')?.addEventListener('click', () => this.endMatching());
|
||
|
||
// Score modal event listeners (not in main modal, so handle separately)
|
||
document.querySelector('.matching-score-modal-close')?.addEventListener('click', () => this.hideScoreModal());
|
||
document.getElementById('matching-score-modal-close-btn')?.addEventListener('click', () => this.hideScoreModal());
|
||
document.getElementById('matching-score-modal-new-game')?.addEventListener('click', () => {
|
||
this.hideScoreModal();
|
||
this.newChallenge();
|
||
});
|
||
|
||
// Close modal when clicking outside
|
||
document.getElementById('matching-score-modal')?.addEventListener('click', (e) => {
|
||
if (e.target.id === 'matching-score-modal') {
|
||
this.hideScoreModal();
|
||
}
|
||
});
|
||
}
|
||
|
||
updateDescription() {
|
||
const description = document.getElementById('matching-description');
|
||
|
||
if (this.gameType === 'complement-pairs') {
|
||
if (this.gameMode === 'two-player') {
|
||
description.textContent = 'Match complement pairs! Find numbers that add up to 5 or 10. Two players take turns finding "friends" that work together.';
|
||
} else {
|
||
description.textContent = 'Match complement pairs! Find numbers that add up to 5 or 10. Each pair contains two numbers that are "friends" - they work together to make 5 or 10.';
|
||
}
|
||
} else {
|
||
if (this.gameMode === 'two-player') {
|
||
description.textContent = 'Match abacus patterns with their corresponding numerals. Find all pairs in minimum moves! Two players take turns.';
|
||
} else {
|
||
description.textContent = 'Match abacus patterns with their corresponding numerals. Find all pairs in minimum moves!';
|
||
}
|
||
}
|
||
}
|
||
|
||
startMatching() {
|
||
// Initialize game cards array
|
||
this.gameCards = [];
|
||
|
||
if (this.gameType === 'complement-pairs') {
|
||
// Create complement pairs for friends of 5 and friends of 10
|
||
const complementPairs = [
|
||
// Friends of 5
|
||
[0, 5], [1, 4], [2, 3],
|
||
// Friends of 10
|
||
[0, 10], [1, 9], [2, 8], [3, 7], [4, 6], [5, 5], [6, 4], [7, 3], [8, 2], [9, 1], [10, 0]
|
||
];
|
||
|
||
// Remove duplicates and limit to requested pairs
|
||
const uniquePairs = [];
|
||
const seen = new Set();
|
||
|
||
for (let pair of complementPairs) {
|
||
const key = Math.min(pair[0], pair[1]) + '_' + Math.max(pair[0], pair[1]);
|
||
if (!seen.has(key)) {
|
||
seen.add(key);
|
||
uniquePairs.push(pair);
|
||
}
|
||
}
|
||
|
||
// Shuffle and select pairs
|
||
const shuffledPairs = uniquePairs.sort(() => Math.random() - 0.5);
|
||
const selectedPairs = shuffledPairs.slice(0, this.selectedPairs);
|
||
|
||
selectedPairs.forEach((pair, index) => {
|
||
// Determine target sum (5 or 10)
|
||
const targetSum = pair[0] + pair[1];
|
||
const label = targetSum === 5 ? '✋' : '🔟';
|
||
|
||
// Add first number card
|
||
this.gameCards.push({
|
||
id: `comp1_${pair[0]}_${pair[1]}`,
|
||
type: 'complement',
|
||
number: pair[0],
|
||
complement: pair[1],
|
||
targetSum: targetSum,
|
||
content: `${pair[0]} ${label}`,
|
||
matched: false
|
||
});
|
||
|
||
// Add second number card
|
||
this.gameCards.push({
|
||
id: `comp2_${pair[0]}_${pair[1]}`,
|
||
type: 'complement',
|
||
number: pair[1],
|
||
complement: pair[0],
|
||
targetSum: targetSum,
|
||
content: `${pair[1]} ${label}`,
|
||
matched: false
|
||
});
|
||
});
|
||
} else {
|
||
// Original matching logic for abacus/number pairs
|
||
// Ensure we have enough cards available
|
||
if (this.cards.length < this.selectedPairs) {
|
||
alert(`Not enough cards available! Need ${this.selectedPairs} unique cards, but only ${this.cards.length} are available.`);
|
||
return;
|
||
}
|
||
|
||
// Select random cards for matching
|
||
const shuffledCards = [...this.cards].sort(() => Math.random() - 0.5);
|
||
const selectedCards = shuffledCards.slice(0, this.selectedPairs);
|
||
|
||
selectedCards.forEach(card => {
|
||
// Add abacus card
|
||
this.gameCards.push({
|
||
id: `abacus_${card.number}`,
|
||
type: 'abacus',
|
||
number: card.number,
|
||
content: card.svg,
|
||
matched: false
|
||
});
|
||
|
||
// Add number card
|
||
this.gameCards.push({
|
||
id: `number_${card.number}`,
|
||
type: 'number',
|
||
number: card.number,
|
||
content: card.number.toString(),
|
||
matched: false
|
||
});
|
||
});
|
||
}
|
||
|
||
// Shuffle the game cards
|
||
this.gameCards = this.gameCards.sort(() => Math.random() - 0.5);
|
||
|
||
// Debug logging
|
||
console.log(`Memory Game Debug:`, {
|
||
selectedPairs: this.selectedPairs,
|
||
availableCards: this.cards.length,
|
||
gameCards: this.gameCards.length,
|
||
expectedCards: this.selectedPairs * 2
|
||
});
|
||
|
||
this.totalPairs = this.selectedPairs;
|
||
this.matchedPairs = 0;
|
||
this.moves = 0;
|
||
this.flippedCards = [];
|
||
|
||
// Initialize two-player mode variables
|
||
if (this.gameMode === 'two-player') {
|
||
this.currentPlayer = 1;
|
||
this.player1Score = 0;
|
||
this.player2Score = 0;
|
||
this.playerTurnActive = true;
|
||
}
|
||
|
||
// Hide configuration controls, show game
|
||
document.querySelector('.matching-controls').style.display = 'none';
|
||
document.getElementById('matching-game').style.display = 'block';
|
||
document.getElementById('matching-header').style.display = 'block';
|
||
|
||
this.renderMatchingGrid();
|
||
this.adjustCardSizes();
|
||
this.startTimer();
|
||
this.setupGameInterface();
|
||
this.updateMatchingStatus(this.gameMode === 'two-player' ? 'Player 1 starts!' : 'Find all matching pairs!');
|
||
this.updateMatchingStats();
|
||
|
||
// For two-player mode with timer, show timer but don't start counting until first move
|
||
if (this.gameMode === 'two-player' && this.turnTimer > 0) {
|
||
this.turnTimeLeft = this.turnTimer;
|
||
this.updateTurnTimerDisplay();
|
||
this.updateTurnTimerStatus('waiting'); // Show timer is waiting for first move
|
||
}
|
||
}
|
||
|
||
renderMatchingGrid() {
|
||
const grid = document.getElementById('matching-grid');
|
||
|
||
// Set grid class based on number of pairs
|
||
grid.className = 'matching-grid';
|
||
if (this.selectedPairs === 6) grid.classList.add('grid-3x4');
|
||
else if (this.selectedPairs === 8) grid.classList.add('grid-4x4');
|
||
else if (this.selectedPairs === 12) grid.classList.add('grid-4x6');
|
||
else if (this.selectedPairs === 15) grid.classList.add('grid-5x6');
|
||
|
||
grid.innerHTML = '';
|
||
|
||
this.gameCards.forEach((card, index) => {
|
||
const cardElement = document.createElement('div');
|
||
cardElement.className = 'match-card';
|
||
cardElement.dataset.index = index;
|
||
cardElement.dataset.cardId = card.id;
|
||
cardElement.dataset.number = card.number;
|
||
|
||
let typeClass, typeIcon, contentHtml;
|
||
|
||
if (card.type === 'complement') {
|
||
// Complement cards have different styling
|
||
const targetSum = card.targetSum;
|
||
typeClass = targetSum === 5 ? 'friends-5-type' : 'friends-10-type';
|
||
typeIcon = ''; // No icon needed - color coding is sufficient
|
||
contentHtml = `<div class="match-card-number complement-number">${card.content}</div>`;
|
||
} else {
|
||
// Original abacus/number cards
|
||
typeClass = card.type === 'abacus' ? 'abacus-type' : 'number-type';
|
||
typeIcon = card.type === 'abacus' ? '🧮' : '🔢';
|
||
contentHtml = card.type === 'number' ?
|
||
`<div class="match-card-number">${card.content}</div>` :
|
||
`<div class="match-card-abacus">${card.content}</div>`;
|
||
}
|
||
|
||
cardElement.innerHTML = `
|
||
<div class="match-card-back ${typeClass}">
|
||
<div class="card-type-icon">${typeIcon}</div>
|
||
</div>
|
||
<div class="match-card-content">
|
||
${contentHtml}
|
||
</div>
|
||
`;
|
||
|
||
cardElement.addEventListener('click', () => this.cardClicked(index));
|
||
grid.appendChild(cardElement);
|
||
});
|
||
}
|
||
|
||
cardClicked(index) {
|
||
const card = this.gameCards[index];
|
||
const cardElement = document.querySelector(`[data-index="${index}"]`);
|
||
|
||
// Ignore if card is already flipped or matched
|
||
if (card.matched || cardElement.classList.contains('flipped')) return;
|
||
|
||
// Ignore if two cards are already flipped
|
||
if (this.flippedCards.length >= 2) return;
|
||
|
||
// In two-player mode, ignore clicks if it's not the player's turn
|
||
if (this.gameMode === 'two-player' && !this.playerTurnActive) return;
|
||
|
||
// Start timer on first move in two-player mode
|
||
if (this.gameMode === 'two-player' && this.turnTimer > 0 && this.isFirstMove) {
|
||
this.isFirstMove = false;
|
||
this.startTurnTimer();
|
||
this.updateTurnTimerStatus('active');
|
||
}
|
||
|
||
// If one card is already flipped, only allow clicking valid pairs
|
||
if (this.flippedCards.length === 1) {
|
||
const flippedCard = this.flippedCards[0].card;
|
||
|
||
if (this.gameType === 'complement-pairs') {
|
||
// For complement pairs, prevent clicking the same card
|
||
if (flippedCard.id === card.id) {
|
||
this.showInvalidMoveHint(cardElement);
|
||
return;
|
||
}
|
||
|
||
// Only allow cards with the same targetSum (friends of 5 with friends of 5, etc.)
|
||
if (card.targetSum !== flippedCard.targetSum) {
|
||
this.showInvalidMoveHint(cardElement);
|
||
return;
|
||
}
|
||
} else {
|
||
// Original logic: only allow opposite types
|
||
const flippedCardType = flippedCard.type;
|
||
if (card.type === flippedCardType) {
|
||
// Same type - show visual feedback but don't flip
|
||
this.showInvalidMoveHint(cardElement);
|
||
return;
|
||
}
|
||
}
|
||
}
|
||
|
||
// Flip the card
|
||
cardElement.classList.add('flipped');
|
||
this.flippedCards.push({ index, element: cardElement, card });
|
||
|
||
// If this is the first card flipped, highlight valid choices
|
||
if (this.flippedCards.length === 1) {
|
||
this.highlightValidCards(card);
|
||
}
|
||
|
||
// If two cards are flipped, check for match
|
||
if (this.flippedCards.length === 2) {
|
||
this.clearHighlights();
|
||
this.stopTurnTimer();
|
||
|
||
if (this.gameMode === 'single') {
|
||
this.moves++;
|
||
}
|
||
|
||
this.updateMatchingStats();
|
||
setTimeout(() => this.checkMatch(), 1000);
|
||
}
|
||
}
|
||
|
||
checkMatch() {
|
||
const [first, second] = this.flippedCards;
|
||
|
||
let isMatch = false;
|
||
|
||
if (this.gameType === 'complement-pairs') {
|
||
// Check if the two numbers are complements
|
||
isMatch = (first.card.number === second.card.complement &&
|
||
second.card.number === first.card.complement &&
|
||
first.card.targetSum === second.card.targetSum);
|
||
} else {
|
||
// Original logic: Check if numbers match and types are different
|
||
isMatch = (first.card.number === second.card.number && first.card.type !== second.card.type);
|
||
}
|
||
|
||
if (isMatch) {
|
||
// Match found!
|
||
first.element.classList.add('matched');
|
||
second.element.classList.add('matched');
|
||
first.card.matched = true;
|
||
second.card.matched = true;
|
||
|
||
// Trigger particle celebration for the match
|
||
if (particleSystem) {
|
||
particleSystem.celebrateSuccess(first.element);
|
||
// Delayed second celebration for visual effect
|
||
setTimeout(() => {
|
||
particleSystem.celebrateSuccess(second.element);
|
||
}, 200);
|
||
}
|
||
|
||
this.matchedPairs++;
|
||
|
||
// In two-player mode, award point to current player and mark cards
|
||
if (this.gameMode === 'two-player') {
|
||
const playerClass = `matched-player${this.currentPlayer}`;
|
||
first.element.classList.add(playerClass);
|
||
second.element.classList.add(playerClass);
|
||
|
||
// Add player badges
|
||
this.addPlayerBadge(first.element, this.currentPlayer);
|
||
this.addPlayerBadge(second.element, this.currentPlayer);
|
||
|
||
if (this.currentPlayer === 1) {
|
||
this.player1Score++;
|
||
} else {
|
||
this.player2Score++;
|
||
}
|
||
// Player gets another turn after a match
|
||
this.updatePlayerStats();
|
||
} else {
|
||
this.updateMatchingStats();
|
||
}
|
||
|
||
// Check if game is complete
|
||
if (this.matchedPairs === this.totalPairs) {
|
||
// Celebrate game completion with major fireworks
|
||
if (particleSystem) {
|
||
const gameArea = document.getElementById('matching-grid');
|
||
if (gameArea) {
|
||
particleSystem.celebrateGameComplete(gameArea);
|
||
}
|
||
}
|
||
this.endGame();
|
||
}
|
||
} else {
|
||
// No match - flip cards back
|
||
first.element.classList.remove('flipped');
|
||
second.element.classList.remove('flipped');
|
||
|
||
// In two-player mode, switch turns after a miss
|
||
if (this.gameMode === 'two-player') {
|
||
this.switchPlayer();
|
||
}
|
||
}
|
||
|
||
this.flippedCards = [];
|
||
this.clearHighlights();
|
||
this.playerTurnActive = true; // Re-enable clicks
|
||
}
|
||
|
||
endGame() {
|
||
this.stopTimer();
|
||
this.stopTurnTimer();
|
||
const elapsedTime = Date.now() - this.gameStartTime;
|
||
const seconds = Math.floor(elapsedTime / 1000);
|
||
|
||
if (this.gameMode === 'two-player') {
|
||
// Two-player results
|
||
let winner, description;
|
||
if (this.player1Score > this.player2Score) {
|
||
winner = 'Player 1 Wins!';
|
||
description = `${this.player1Score} - ${this.player2Score}`;
|
||
} else if (this.player2Score > this.player1Score) {
|
||
winner = 'Player 2 Wins!';
|
||
description = `${this.player2Score} - ${this.player1Score}`;
|
||
} else {
|
||
winner = "It's a Tie!";
|
||
description = `${this.player1Score} - ${this.player2Score}`;
|
||
}
|
||
|
||
this.showScoreModal({
|
||
gameMode: 'two-player',
|
||
winner,
|
||
description,
|
||
player1Score: this.player1Score,
|
||
player2Score: this.player2Score,
|
||
totalPairs: this.totalPairs,
|
||
time: seconds
|
||
});
|
||
} else {
|
||
// Single player results
|
||
const minimumMoves = this.totalPairs;
|
||
const efficiency = this.moves / minimumMoves;
|
||
|
||
let medal, description;
|
||
if (efficiency <= 1.5) {
|
||
medal = '🏆 Gold';
|
||
description = 'Perfect efficiency!';
|
||
} else if (efficiency <= 2.0) {
|
||
medal = '🥈 Silver';
|
||
description = 'Great job!';
|
||
} else if (efficiency <= 3.0) {
|
||
medal = '🥉 Bronze';
|
||
description = 'Good effort!';
|
||
} else {
|
||
medal = '🎯 Practice';
|
||
description = 'Keep practicing!';
|
||
}
|
||
|
||
this.showScoreModal({
|
||
gameMode: 'single',
|
||
pairs: this.totalPairs,
|
||
moves: this.moves,
|
||
time: seconds,
|
||
efficiency: efficiency.toFixed(1),
|
||
medal,
|
||
description
|
||
});
|
||
}
|
||
}
|
||
|
||
showScoreModal(results) {
|
||
const modal = document.getElementById('matching-score-modal');
|
||
const body = document.getElementById('matching-score-modal-body');
|
||
|
||
const timeStr = this.formatTime(results.time);
|
||
|
||
if (results.gameMode === 'two-player') {
|
||
// Two-player results layout
|
||
body.innerHTML = `
|
||
<div class="score-summary">
|
||
<div class="score-medal">${results.winner}</div>
|
||
<div class="score-description">${results.description}</div>
|
||
</div>
|
||
|
||
<div class="two-player-results">
|
||
<div class="player-result">
|
||
<div class="player-name">🔵 Player 1</div>
|
||
<div class="player-final-score" style="color: #007bff;">${results.player1Score} pairs</div>
|
||
</div>
|
||
<div class="vs-divider">VS</div>
|
||
<div class="player-result">
|
||
<div class="player-name">🟠 Player 2</div>
|
||
<div class="player-final-score" style="color: #fd7e14;">${results.player2Score} pairs</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="game-summary">
|
||
<div class="summary-item">
|
||
<span class="summary-label">Total Pairs:</span>
|
||
<span class="summary-value">${results.totalPairs}</span>
|
||
</div>
|
||
<div class="summary-item">
|
||
<span class="summary-label">Game Time:</span>
|
||
<span class="summary-value">${timeStr}</span>
|
||
</div>
|
||
</div>
|
||
`;
|
||
} else {
|
||
// Single player results layout
|
||
body.innerHTML = `
|
||
<div class="score-summary">
|
||
<div class="score-medal">${results.medal}</div>
|
||
<div class="score-description">${results.description}</div>
|
||
</div>
|
||
|
||
<div class="score-details">
|
||
<div class="score-item">
|
||
<span class="score-label">Pairs Matched:</span>
|
||
<span class="score-value">${results.pairs}</span>
|
||
</div>
|
||
<div class="score-item">
|
||
<span class="score-label">Total Moves:</span>
|
||
<span class="score-value">${results.moves}</span>
|
||
</div>
|
||
<div class="score-item">
|
||
<span class="score-label">Time:</span>
|
||
<span class="score-value">${timeStr}</span>
|
||
</div>
|
||
<div class="score-item">
|
||
<span class="score-label">Efficiency:</span>
|
||
<span class="score-value">${results.efficiency}x</span>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="score-explanation">
|
||
<h4>Scoring System</h4>
|
||
<div class="score-tiers">
|
||
<div>🏆 Gold: ≤1.5x minimum moves</div>
|
||
<div>🥈 Silver: ≤2.0x minimum moves</div>
|
||
<div>🥉 Bronze: ≤3.0x minimum moves</div>
|
||
<div>🎯 Practice: >3.0x minimum moves</div>
|
||
</div>
|
||
</div>
|
||
`;
|
||
}
|
||
|
||
modal.style.display = 'block';
|
||
}
|
||
|
||
hideScoreModal() {
|
||
document.getElementById('matching-score-modal').style.display = 'none';
|
||
}
|
||
|
||
newChallenge() {
|
||
this.endMatching();
|
||
setTimeout(() => this.startMatching(), 100);
|
||
}
|
||
|
||
endMatching() {
|
||
// Stop timer
|
||
this.stopTimer();
|
||
this.stopTurnTimer();
|
||
|
||
// Hide game and sticky header, show configuration controls
|
||
document.getElementById('matching-game').style.display = 'none';
|
||
document.getElementById('matching-header').style.display = 'none';
|
||
document.querySelector('.matching-controls').style.display = 'block';
|
||
|
||
// Reset game state
|
||
this.flippedCards = [];
|
||
this.matchedPairs = 0;
|
||
this.moves = 0;
|
||
this.currentPlayer = 1;
|
||
this.player1Score = 0;
|
||
this.player2Score = 0;
|
||
this.playerTurnActive = true;
|
||
this.isFirstMove = true;
|
||
|
||
// Reset mode selection display but preserve selected values
|
||
this.updateModeDisplay();
|
||
|
||
// Re-bind events to ensure they work after DOM changes
|
||
this.bindMatchingEvents();
|
||
}
|
||
|
||
updateModeDisplay() {
|
||
// Ensure the current game mode button is properly highlighted
|
||
document.querySelectorAll('.mode-btn').forEach(btn => {
|
||
btn.classList.remove('active');
|
||
if (btn.dataset.mode === this.gameMode) {
|
||
btn.classList.add('active');
|
||
}
|
||
});
|
||
|
||
// Ensure the current game type button is properly highlighted
|
||
document.querySelectorAll('.game-type-btn').forEach(btn => {
|
||
btn.classList.remove('active');
|
||
if (btn.dataset.gameType === this.gameType) {
|
||
btn.classList.add('active');
|
||
}
|
||
});
|
||
|
||
// Show/hide timer controls based on current mode
|
||
const timerControls = document.getElementById('timer-controls');
|
||
if (this.gameMode === 'two-player') {
|
||
timerControls.style.display = 'block';
|
||
} else {
|
||
timerControls.style.display = 'none';
|
||
}
|
||
|
||
// Ensure timer button is highlighted
|
||
document.querySelectorAll('.timer-btn').forEach(btn => {
|
||
btn.classList.remove('active');
|
||
if (parseInt(btn.dataset.timer) === this.turnTimer) {
|
||
btn.classList.add('active');
|
||
}
|
||
});
|
||
}
|
||
|
||
startTimer() {
|
||
this.gameStartTime = Date.now();
|
||
this.timerInterval = setInterval(() => {
|
||
const elapsed = Date.now() - this.gameStartTime;
|
||
const seconds = Math.floor(elapsed / 1000);
|
||
document.getElementById('matching-timer').textContent = this.formatTime(seconds);
|
||
}, 1000);
|
||
}
|
||
|
||
stopTimer() {
|
||
if (this.timerInterval) {
|
||
clearInterval(this.timerInterval);
|
||
this.timerInterval = null;
|
||
}
|
||
}
|
||
|
||
formatTime(totalSeconds) {
|
||
const minutes = Math.floor(totalSeconds / 60);
|
||
const seconds = totalSeconds % 60;
|
||
return `${minutes}:${seconds.toString().padStart(2, '0')}`;
|
||
}
|
||
|
||
updateMatchingStatus(message) {
|
||
document.getElementById('matching-status').textContent = message;
|
||
}
|
||
|
||
updateMatchingStats() {
|
||
document.getElementById('moves-counter').textContent = `Moves: ${this.moves}`;
|
||
document.getElementById('pairs-found').textContent = `Pairs: ${this.matchedPairs}/${this.totalPairs}`;
|
||
}
|
||
|
||
adjustCardSizes() {
|
||
// CSS now handles responsive sizing automatically
|
||
// This method can be kept for any future dynamic adjustments if needed
|
||
|
||
// Just ensure font sizes scale with container
|
||
const grid = document.getElementById('matching-grid');
|
||
const cards = grid.querySelectorAll('.match-card');
|
||
|
||
cards.forEach(card => {
|
||
const cardRect = card.getBoundingClientRect();
|
||
const cardSize = Math.min(cardRect.width, cardRect.height);
|
||
|
||
// Adjust font sizes proportionally to actual card size
|
||
const baseFontSize = Math.max(cardSize * 0.25, 12);
|
||
const iconSize = Math.max(cardSize * 0.4, 16);
|
||
|
||
const number = card.querySelector('.match-card-number');
|
||
if (number) number.style.fontSize = `${baseFontSize}px`;
|
||
|
||
const icon = card.querySelector('.card-type-icon');
|
||
if (icon) icon.style.fontSize = `${iconSize}px`;
|
||
});
|
||
}
|
||
|
||
showInvalidMoveHint(cardElement) {
|
||
// Add temporary visual feedback for invalid moves
|
||
cardElement.classList.add('invalid-move');
|
||
|
||
// Remove the class after animation
|
||
setTimeout(() => {
|
||
cardElement.classList.remove('invalid-move');
|
||
}, 600);
|
||
}
|
||
|
||
highlightValidCards(flippedCard) {
|
||
if (this.gameType === 'complement-pairs') {
|
||
// For complement pairs, highlight cards with the same targetSum only
|
||
const flippedTargetSum = flippedCard.targetSum;
|
||
|
||
this.gameCards.forEach((card, index) => {
|
||
const cardElement = document.querySelector(`[data-index="${index}"]`);
|
||
if (!card.matched && !cardElement.classList.contains('flipped')) {
|
||
if (card.targetSum === flippedTargetSum) {
|
||
cardElement.classList.add('valid-choice');
|
||
} else {
|
||
cardElement.classList.add('invalid-choice');
|
||
}
|
||
}
|
||
});
|
||
} else {
|
||
// For abacus/numeral pairs, highlight cards of the opposite type
|
||
const flippedCardType = flippedCard.type;
|
||
const validType = flippedCardType === 'abacus' ? 'number' : 'abacus';
|
||
|
||
this.gameCards.forEach((card, index) => {
|
||
const cardElement = document.querySelector(`[data-index="${index}"]`);
|
||
if (!card.matched && !cardElement.classList.contains('flipped')) {
|
||
if (card.type === validType) {
|
||
cardElement.classList.add('valid-choice');
|
||
} else {
|
||
cardElement.classList.add('invalid-choice');
|
||
}
|
||
}
|
||
});
|
||
}
|
||
}
|
||
|
||
clearHighlights() {
|
||
// Remove all highlight classes
|
||
const cards = document.querySelectorAll('.match-card');
|
||
cards.forEach(card => {
|
||
card.classList.remove('valid-choice', 'invalid-choice');
|
||
});
|
||
}
|
||
|
||
setupGameInterface() {
|
||
// Show/hide appropriate UI elements based on game mode
|
||
const singleStats = document.querySelector('.matching-stats');
|
||
const playerStats = document.getElementById('player-stats');
|
||
|
||
if (this.gameMode === 'two-player') {
|
||
singleStats.style.display = 'none';
|
||
playerStats.style.display = 'flex';
|
||
this.updatePlayerStats();
|
||
|
||
// Show turn timer if enabled
|
||
const turnTimer = document.getElementById('turn-timer');
|
||
if (this.turnTimer > 0) {
|
||
turnTimer.style.display = 'block';
|
||
turnTimer.textContent = `${this.turnTimer}s`;
|
||
} else {
|
||
turnTimer.style.display = 'none';
|
||
}
|
||
} else {
|
||
singleStats.style.display = 'flex';
|
||
playerStats.style.display = 'none';
|
||
}
|
||
}
|
||
|
||
updatePlayerStats() {
|
||
// Update player scores and current turn indicator
|
||
document.querySelector('#player1-info .player-score').textContent = `${this.player1Score} pairs`;
|
||
document.querySelector('#player2-info .player-score').textContent = `${this.player2Score} pairs`;
|
||
|
||
// Update active player highlighting
|
||
const player1Info = document.getElementById('player1-info');
|
||
const player2Info = document.getElementById('player2-info');
|
||
const currentTurn = document.getElementById('current-turn');
|
||
|
||
if (this.currentPlayer === 1) {
|
||
player1Info.classList.add('active');
|
||
player2Info.classList.remove('active');
|
||
currentTurn.textContent = "🔵 Player 1's Turn";
|
||
} else {
|
||
player1Info.classList.remove('active');
|
||
player2Info.classList.add('active');
|
||
currentTurn.textContent = "🟠 Player 2's Turn";
|
||
}
|
||
}
|
||
|
||
addPlayerBadge(cardElement, player) {
|
||
// Create player badge element
|
||
const badge = document.createElement('div');
|
||
badge.className = `player-badge player${player}`;
|
||
badge.textContent = player;
|
||
|
||
// Add badge to card
|
||
cardElement.appendChild(badge);
|
||
}
|
||
|
||
switchPlayer() {
|
||
this.currentPlayer = this.currentPlayer === 1 ? 2 : 1;
|
||
this.updatePlayerStats();
|
||
this.updateMatchingStatus(`Player ${this.currentPlayer}'s turn`);
|
||
|
||
// Start turn timer immediately for new player if enabled
|
||
if (this.turnTimer > 0) {
|
||
this.startTurnTimer();
|
||
}
|
||
}
|
||
|
||
startTurnTimer() {
|
||
if (this.turnTimer <= 0) return;
|
||
|
||
this.turnTimeLeft = this.turnTimer;
|
||
this.updateTurnTimerDisplay();
|
||
|
||
this.turnTimerInterval = setInterval(() => {
|
||
this.turnTimeLeft--;
|
||
this.updateTurnTimerDisplay();
|
||
|
||
if (this.turnTimeLeft <= 0) {
|
||
this.handleTurnTimeout();
|
||
}
|
||
}, 1000);
|
||
}
|
||
|
||
stopTurnTimer() {
|
||
if (this.turnTimerInterval) {
|
||
clearInterval(this.turnTimerInterval);
|
||
this.turnTimerInterval = null;
|
||
}
|
||
}
|
||
|
||
updateTurnTimerDisplay() {
|
||
const timerElement = document.getElementById('turn-timer');
|
||
if (!timerElement || this.turnTimer <= 0) return;
|
||
|
||
timerElement.textContent = `${this.turnTimeLeft}s`;
|
||
|
||
// Add warning/critical classes based on time left
|
||
timerElement.classList.remove('warning', 'critical');
|
||
if (this.turnTimeLeft <= 5) {
|
||
timerElement.classList.add('critical');
|
||
} else if (this.turnTimeLeft <= 10) {
|
||
timerElement.classList.add('warning');
|
||
}
|
||
}
|
||
|
||
updateTurnTimerStatus(status) {
|
||
const timerElement = document.getElementById('turn-timer');
|
||
if (!timerElement) return;
|
||
|
||
if (status === 'waiting') {
|
||
timerElement.classList.add('waiting');
|
||
const timeString = `${this.turnTimeLeft}s`;
|
||
timerElement.textContent = `⏸️ ${timeString}`;
|
||
} else if (status === 'active') {
|
||
timerElement.classList.remove('waiting');
|
||
this.updateTurnTimerDisplay();
|
||
}
|
||
}
|
||
|
||
handleTurnTimeout() {
|
||
this.stopTurnTimer();
|
||
this.playerTurnActive = false;
|
||
|
||
// Clear any flipped cards
|
||
if (this.flippedCards.length > 0) {
|
||
this.flippedCards.forEach(item => {
|
||
item.element.classList.remove('flipped');
|
||
});
|
||
this.flippedCards = [];
|
||
this.clearHighlights();
|
||
}
|
||
|
||
// Switch to next player
|
||
setTimeout(() => {
|
||
this.switchPlayer();
|
||
this.playerTurnActive = true;
|
||
}, 500);
|
||
}
|
||
}
|
||
|
||
// Function to handle print requests by opening PDF in print dialog
|
||
function handlePrintRequest() {
|
||
console.log('Print request intercepted!');
|
||
// Get the companion PDF URL
|
||
const pdfUrl = window.location.href.replace(/\.html$/, '.pdf');
|
||
|
||
// Create a modal overlay explaining the situation
|
||
const modal = document.createElement('div');
|
||
modal.style.cssText = `
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
background: rgba(0, 0, 0, 0.8);
|
||
z-index: 10000;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
`;
|
||
|
||
const content = document.createElement('div');
|
||
content.style.cssText = `
|
||
background: white;
|
||
border-radius: 8px;
|
||
padding: 30px;
|
||
max-width: 500px;
|
||
text-align: center;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
`;
|
||
|
||
const title = document.createElement('h3');
|
||
title.textContent = 'Print High-Quality Flashcards';
|
||
title.style.margin = '0 0 20px 0';
|
||
title.style.color = '#333';
|
||
|
||
const explanation = document.createElement('p');
|
||
explanation.innerHTML = 'The web version is not optimized for printing.<br>For best results, download the PDF version which includes:<br><br>• Proper card sizing and layout<br>• Cut marks for easy trimming<br>• Registration marks for alignment';
|
||
explanation.style.textAlign = 'left';
|
||
explanation.style.margin = '0 0 25px 0';
|
||
explanation.style.lineHeight = '1.6';
|
||
explanation.style.color = '#555';
|
||
|
||
const buttonContainer = document.createElement('div');
|
||
buttonContainer.style.cssText = 'display: flex; gap: 15px;';
|
||
|
||
const downloadButton = document.createElement('button');
|
||
downloadButton.textContent = '📄 Open PDF';
|
||
downloadButton.style.cssText = `
|
||
padding: 12px 24px;
|
||
background: #007bff;
|
||
color: white;
|
||
border: none;
|
||
border-radius: 6px;
|
||
cursor: pointer;
|
||
font-size: 16px;
|
||
font-weight: 500;
|
||
`;
|
||
|
||
const cancelButton = document.createElement('button');
|
||
cancelButton.textContent = 'Cancel';
|
||
cancelButton.style.cssText = `
|
||
padding: 12px 24px;
|
||
background: #6c757d;
|
||
color: white;
|
||
border: none;
|
||
border-radius: 6px;
|
||
cursor: pointer;
|
||
font-size: 16px;
|
||
`;
|
||
|
||
// Download button handler
|
||
downloadButton.onclick = function() {
|
||
showNotification('📄 Opening PDF in new tab...');
|
||
window.open(pdfUrl, '_blank');
|
||
modal.remove();
|
||
};
|
||
|
||
// Cancel button handler
|
||
cancelButton.onclick = function() {
|
||
modal.remove();
|
||
};
|
||
|
||
// Close on backdrop click
|
||
modal.onclick = function(e) {
|
||
if (e.target === modal) {
|
||
modal.remove();
|
||
}
|
||
};
|
||
|
||
// Prevent clicks inside content from closing modal
|
||
content.onclick = function(e) {
|
||
e.stopPropagation();
|
||
};
|
||
|
||
// Assemble the modal
|
||
buttonContainer.appendChild(downloadButton);
|
||
buttonContainer.appendChild(cancelButton);
|
||
content.appendChild(title);
|
||
content.appendChild(explanation);
|
||
content.appendChild(buttonContainer);
|
||
modal.appendChild(content);
|
||
document.body.appendChild(modal);
|
||
}
|
||
|
||
// Fallback function to download PDF
|
||
function downloadPDF(pdfUrl) {
|
||
const link = document.createElement('a');
|
||
link.href = pdfUrl;
|
||
link.download = pdfUrl.split('/').pop();
|
||
document.body.appendChild(link);
|
||
link.click();
|
||
document.body.removeChild(link);
|
||
}
|
||
|
||
// Function to show brief notifications
|
||
function showNotification(message) {
|
||
const notification = document.createElement('div');
|
||
notification.style.cssText = `
|
||
position: fixed;
|
||
top: 20px;
|
||
right: 20px;
|
||
background: #28a745;
|
||
color: white;
|
||
padding: 15px 20px;
|
||
border-radius: 8px;
|
||
font-size: 16px;
|
||
font-weight: 500;
|
||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||
z-index: 10000;
|
||
animation: slideInFromRight 0.3s ease-out;
|
||
`;
|
||
notification.textContent = message;
|
||
|
||
// Add animation styles
|
||
const style = document.createElement('style');
|
||
style.textContent = `
|
||
@keyframes slideInFromRight {
|
||
from { transform: translateX(100%); opacity: 0; }
|
||
to { transform: translateX(0); opacity: 1; }
|
||
}
|
||
`;
|
||
document.head.appendChild(style);
|
||
|
||
document.body.appendChild(notification);
|
||
|
||
// Auto-remove after 3 seconds
|
||
setTimeout(() => {
|
||
notification.style.animation = 'slideInFromRight 0.3s ease-out reverse';
|
||
setTimeout(() => {
|
||
if (notification.parentNode) {
|
||
document.body.removeChild(notification);
|
||
}
|
||
if (style.parentNode) {
|
||
document.head.removeChild(style);
|
||
}
|
||
}, 300);
|
||
}, 3000);
|
||
}
|
||
|
||
// Section Navigation Manager
|
||
class SectionNavigator {
|
||
constructor() {
|
||
this.currentSection = 'welcome';
|
||
this.currentGuideSection = 'reading';
|
||
this.setupEventListeners();
|
||
}
|
||
|
||
setupEventListeners() {
|
||
// Main navigation
|
||
document.querySelectorAll('.nav-btn').forEach(btn => {
|
||
btn.addEventListener('click', (e) => {
|
||
const section = e.target.getAttribute('data-section');
|
||
this.showSection(section);
|
||
});
|
||
});
|
||
|
||
// Guide navigation
|
||
document.querySelectorAll('.guide-nav-btn').forEach(btn => {
|
||
btn.addEventListener('click', (e) => {
|
||
const guideSection = e.target.getAttribute('data-guide-section');
|
||
this.showGuideSection(guideSection);
|
||
});
|
||
});
|
||
|
||
// Feature buttons in welcome section
|
||
document.querySelectorAll('.feature-btn').forEach(btn => {
|
||
btn.addEventListener('click', (e) => {
|
||
const section = e.target.getAttribute('data-section');
|
||
this.showSection(section);
|
||
});
|
||
});
|
||
}
|
||
|
||
showSection(sectionId) {
|
||
// Hide all sections
|
||
document.querySelectorAll('.page-section').forEach(section => {
|
||
section.classList.remove('active');
|
||
});
|
||
|
||
// Remove active from all nav buttons
|
||
document.querySelectorAll('.nav-btn').forEach(btn => {
|
||
btn.classList.remove('active');
|
||
});
|
||
|
||
// Show selected section
|
||
const targetSection = document.getElementById(sectionId);
|
||
if (targetSection) {
|
||
targetSection.classList.add('active');
|
||
}
|
||
|
||
// Activate corresponding nav button
|
||
const targetBtn = document.querySelector(`[data-section="${sectionId}"]`);
|
||
if (targetBtn) {
|
||
targetBtn.classList.add('active');
|
||
}
|
||
|
||
this.currentSection = sectionId;
|
||
|
||
// Scroll to top when switching sections
|
||
window.scrollTo({ top: 0, behavior: 'smooth' });
|
||
}
|
||
|
||
showGuideSection(guideSectionId) {
|
||
// Hide all guide sections
|
||
document.querySelectorAll('.guide-section').forEach(section => {
|
||
section.classList.remove('active');
|
||
});
|
||
|
||
// Remove active from all guide nav buttons
|
||
document.querySelectorAll('.guide-nav-btn').forEach(btn => {
|
||
btn.classList.remove('active');
|
||
});
|
||
|
||
// Show selected guide section
|
||
const targetSection = document.getElementById(guideSectionId);
|
||
if (targetSection) {
|
||
targetSection.classList.add('active');
|
||
}
|
||
|
||
// Activate corresponding guide nav button
|
||
const targetBtn = document.querySelector(`[data-guide-section="${guideSectionId}"]`);
|
||
if (targetBtn) {
|
||
targetBtn.classList.add('active');
|
||
}
|
||
|
||
this.currentGuideSection = guideSectionId;
|
||
|
||
// Smooth scroll to guide content
|
||
const guideContent = document.querySelector('.guide-content');
|
||
if (guideContent) {
|
||
guideContent.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
||
}
|
||
}
|
||
}
|
||
|
||
// Tutorial functionality
|
||
function revealAnswer() {
|
||
const label = document.getElementById('practice-label');
|
||
const btn = document.getElementById('reveal-answer');
|
||
const example = document.getElementById('practice-example');
|
||
|
||
if (label && btn && example) {
|
||
label.textContent = 'Practice Number: 456';
|
||
btn.textContent = 'Try Another!';
|
||
btn.onclick = function() {
|
||
// Generate new random number for practice
|
||
const randomNum = Math.floor(Math.random() * 900) + 100;
|
||
label.textContent = 'Practice Number';
|
||
btn.textContent = 'Reveal Answer';
|
||
btn.onclick = revealAnswer;
|
||
|
||
// Add reveal animation
|
||
example.classList.add('revealed');
|
||
setTimeout(() => example.classList.remove('revealed'), 500);
|
||
};
|
||
|
||
// Add reveal animation
|
||
example.classList.add('revealed');
|
||
setTimeout(() => example.classList.remove('revealed'), 500);
|
||
}
|
||
}
|
||
|
||
// Initialize quiz and sorting when DOM is loaded
|
||
// Speed Complement Race Challenge
|
||
class SpeedComplementRace {
|
||
constructor() {
|
||
this.mode = 'friends5'; // 'friends5', 'friends10', 'mixed'
|
||
this.style = 'practice'; // 'practice', 'sprint', 'survival'
|
||
this.timeoutSetting = 'normal'; // 'preschool', 'kindergarten', 'relaxed', 'slow', 'normal', 'fast', 'expert'
|
||
this.currentNumber = 0; // Initialize with default value instead of null
|
||
this.targetSum = 5;
|
||
this.correctAnswer = 0; // Initialize with default value instead of null
|
||
this.previousQuestion = null; // Track previous question to avoid repeats
|
||
this.score = 0;
|
||
this.streak = 0;
|
||
this.bestStreak = 0;
|
||
this.currentInput = '';
|
||
this.isGameActive = false;
|
||
this.isPaused = false;
|
||
this.autoSubmitTimeout = null;
|
||
this.totalQuestions = 0;
|
||
this.correctAnswers = 0;
|
||
this.gameStartTime = null;
|
||
this.questionStartTime = 0;
|
||
this.timerInterval = null;
|
||
this.gameInterval = null;
|
||
this.isScrolling = false;
|
||
this.isResetting = false;
|
||
|
||
// Lap tracking for circular race celebrations
|
||
this.playerLap = 0;
|
||
this.aiLaps = new Map(); // Track each AI's lap count
|
||
this.lapCelebrationCooldown = new Set(); // Prevent multiple celebrations for same lap
|
||
|
||
// Adaptive Difficulty System
|
||
this.difficultyTracker = {
|
||
// Track performance for each complement pair
|
||
pairPerformance: new Map(), // key: "number_complement_targetSum", value: { attempts, correct, avgTime, difficulty }
|
||
baseTimeLimit: 3000, // Base time limit in milliseconds
|
||
currentTimeLimit: 3000,
|
||
difficultyLevel: 1, // 1-5 scale
|
||
consecutiveCorrect: 0,
|
||
consecutiveIncorrect: 0,
|
||
learningMode: true, // Gives extra time initially
|
||
adaptationRate: 0.1, // How quickly to adapt (0.1 = gradual)
|
||
};
|
||
|
||
// Race properties
|
||
this.raceGoal = 20;
|
||
this.aiRacers = [
|
||
{
|
||
id: 'ai-racer-1',
|
||
position: 2,
|
||
speed: 0.25,
|
||
name: 'Swift AI',
|
||
personality: 'competitive',
|
||
icon: '🏃♂️',
|
||
lastComment: 0,
|
||
commentCooldown: 0,
|
||
previousPosition: 2
|
||
},
|
||
{
|
||
id: 'ai-racer-2',
|
||
position: 2,
|
||
speed: 0.15,
|
||
name: 'Math Bot',
|
||
personality: 'analytical',
|
||
icon: '🏃',
|
||
lastComment: 0,
|
||
commentCooldown: 0,
|
||
previousPosition: 2
|
||
}
|
||
];
|
||
this.previousPlayerProgress = 0;
|
||
this.aiUpdateInterval = null;
|
||
|
||
this.bindEvents();
|
||
this.updateTargetSum(); // Initialize target sum display
|
||
}
|
||
|
||
bindEvents() {
|
||
// Mode selection buttons
|
||
document.querySelectorAll('.mode-btn[data-mode]').forEach(btn => {
|
||
btn.addEventListener('click', (e) => {
|
||
document.querySelectorAll('.mode-btn').forEach(b => b.classList.remove('active'));
|
||
btn.classList.add('active');
|
||
this.mode = btn.dataset.mode;
|
||
this.updateTargetSum();
|
||
});
|
||
});
|
||
|
||
// Timeout selection buttons
|
||
document.querySelectorAll('.timeout-btn[data-timeout]').forEach(btn => {
|
||
btn.addEventListener('click', (e) => {
|
||
document.querySelectorAll('.timeout-btn').forEach(b => b.classList.remove('active'));
|
||
btn.classList.add('active');
|
||
this.timeoutSetting = btn.dataset.timeout;
|
||
console.log('Timeout setting changed to:', this.timeoutSetting);
|
||
});
|
||
});
|
||
|
||
// Style selection buttons - start game directly when clicked
|
||
document.querySelectorAll('.game-style-btn[data-style]').forEach(btn => {
|
||
btn.addEventListener('click', (e) => {
|
||
// Update active state
|
||
document.querySelectorAll('.game-style-btn').forEach(b => b.classList.remove('active'));
|
||
btn.classList.add('active');
|
||
|
||
// Set style and start game immediately
|
||
this.style = btn.dataset.style;
|
||
this.startRace();
|
||
});
|
||
});
|
||
|
||
// Keystroke capture for the entire game area
|
||
document.addEventListener('keydown', (e) => this.handleKeydown(e));
|
||
|
||
// Game control buttons
|
||
document.getElementById('pause-complement').addEventListener('click', () => this.pauseRace());
|
||
document.getElementById('end-complement').addEventListener('click', () => this.endRace());
|
||
|
||
// Score modal events
|
||
document.querySelector('.complement-score-modal-close').addEventListener('click', () => {
|
||
document.getElementById('complement-score-modal').style.display = 'none';
|
||
});
|
||
document.getElementById('complement-score-modal-close-btn').addEventListener('click', () => {
|
||
document.getElementById('complement-score-modal').style.display = 'none';
|
||
});
|
||
document.getElementById('complement-score-modal-new-game').addEventListener('click', () => {
|
||
document.getElementById('complement-score-modal').style.display = 'none';
|
||
this.startRace();
|
||
});
|
||
}
|
||
|
||
updateTargetSum() {
|
||
if (this.mode === 'friends5') {
|
||
this.targetSum = 5;
|
||
} else if (this.mode === 'friends10') {
|
||
this.targetSum = 10;
|
||
} else if (this.mode === 'mixed') {
|
||
// For mixed mode, we'll set it during nextQuestion, but initialize to 5
|
||
this.targetSum = 5;
|
||
}
|
||
document.getElementById('target-number').textContent = this.targetSum;
|
||
}
|
||
|
||
startRace() {
|
||
this.isGameActive = false; // Don't start until countdown finishes
|
||
this.isPaused = false;
|
||
this.gameStartTime = null; // Will be set after countdown
|
||
this.score = 0;
|
||
this.streak = 0;
|
||
this.totalQuestions = 0;
|
||
this.correctAnswers = 0;
|
||
this.previousQuestion = null; // Reset previous question for new race
|
||
|
||
// Hide intro and controls, show game
|
||
document.querySelector('.complement-intro').style.display = 'none';
|
||
document.querySelector('.complement-controls').style.display = 'none';
|
||
document.getElementById('complement-game').style.display = 'block';
|
||
document.getElementById('complement-header').style.display = 'flex';
|
||
|
||
// Hide timer bar for train variant - use pressure system instead
|
||
const timerSection = document.querySelector('.timer-section');
|
||
if (timerSection) {
|
||
if (this.style === 'sprint') {
|
||
timerSection.style.display = 'none';
|
||
} else {
|
||
timerSection.style.display = 'block';
|
||
}
|
||
}
|
||
|
||
// Configure visualization based on game style
|
||
const raceTrackSection = document.querySelector('.race-track-section');
|
||
const raceTrack = document.querySelector('.race-track');
|
||
const tunnelContainer = document.querySelector('.tunnel-container');
|
||
|
||
if (this.style === 'survival') {
|
||
// Switch to circular track for survival mode
|
||
raceTrackSection.classList.add('circular-track');
|
||
raceTrack.classList.add('circular');
|
||
raceTrackSection.classList.remove('tunnel-digging');
|
||
raceTrack.style.display = 'block';
|
||
if (tunnelContainer) tunnelContainer.style.display = 'none';
|
||
} else if (this.style === 'sprint') {
|
||
// Switch to steam train journey for sprint mode
|
||
raceTrackSection.classList.add('steam-journey');
|
||
raceTrackSection.classList.remove('circular-track');
|
||
raceTrack.classList.remove('circular');
|
||
raceTrack.style.display = 'none'; // Hide track completely
|
||
const steamJourney = document.getElementById('steam-journey');
|
||
if (steamJourney) {
|
||
steamJourney.style.display = 'block';
|
||
// Initialize steam train journey
|
||
this.initializeSteamJourney();
|
||
}
|
||
} else {
|
||
// Use linear track for practice mode
|
||
raceTrackSection.classList.remove('circular-track', 'tunnel-digging');
|
||
raceTrack.classList.remove('circular');
|
||
raceTrack.style.display = 'block';
|
||
if (tunnelContainer) tunnelContainer.style.display = 'none';
|
||
}
|
||
|
||
// Initialize race system
|
||
this.initializeRace();
|
||
|
||
// Start countdown before race begins
|
||
this.startCountdown();
|
||
}
|
||
|
||
startCountdown() {
|
||
const countdownOverlay = document.getElementById('race-countdown');
|
||
const countdownNumber = document.getElementById('countdown-number');
|
||
const countdownText = document.getElementById('countdown-text');
|
||
|
||
// Show countdown overlay
|
||
countdownOverlay.style.display = 'flex';
|
||
|
||
let count = 3;
|
||
const raceTypes = {
|
||
'practice': 'Practice Race',
|
||
'sprint': 'Speed Sprint',
|
||
'survival': 'Survival Challenge'
|
||
};
|
||
|
||
countdownText.textContent = `${raceTypes[this.style]} Starting...`;
|
||
|
||
const countdownInterval = setInterval(() => {
|
||
if (count > 0) {
|
||
countdownNumber.textContent = count;
|
||
countdownNumber.className = 'countdown-number';
|
||
|
||
// Play countdown sound
|
||
this.playSound('countdown', 0.4);
|
||
|
||
count--;
|
||
} else {
|
||
// Show "GO!"
|
||
countdownNumber.textContent = 'GO!';
|
||
countdownNumber.className = 'countdown-go';
|
||
countdownText.textContent = 'Race in Progress!';
|
||
|
||
// Play start sound
|
||
this.playSound('race_start', 0.6);
|
||
|
||
// Hide countdown after GO animation
|
||
setTimeout(() => {
|
||
countdownOverlay.style.display = 'none';
|
||
|
||
// Actually start the race now
|
||
this.isGameActive = true;
|
||
this.gameStartTime = Date.now();
|
||
this.nextQuestion();
|
||
this.startGameTimer();
|
||
}, 1000);
|
||
|
||
clearInterval(countdownInterval);
|
||
}
|
||
}, 1000);
|
||
}
|
||
|
||
nextQuestion() {
|
||
if (!this.isGameActive) return;
|
||
|
||
// Clear any existing question timer first
|
||
if (this.timerInterval) {
|
||
clearInterval(this.timerInterval);
|
||
this.timerInterval = null;
|
||
}
|
||
|
||
// Update target sum for mixed mode
|
||
if (this.mode === 'mixed') {
|
||
this.targetSum = Math.random() > 0.5 ? 5 : 10;
|
||
document.getElementById('target-number').textContent = this.targetSum;
|
||
}
|
||
|
||
// Generate question - avoid repeating the same question
|
||
let newNumber, newTargetSum = this.targetSum;
|
||
let attempts = 0;
|
||
|
||
do {
|
||
if (newTargetSum === 5) {
|
||
newNumber = Math.floor(Math.random() * 5); // 0-4
|
||
} else {
|
||
newNumber = Math.floor(Math.random() * 10); // 0-9
|
||
}
|
||
attempts++;
|
||
} while (
|
||
this.previousQuestion &&
|
||
this.previousQuestion.number === newNumber &&
|
||
this.previousQuestion.targetSum === newTargetSum &&
|
||
attempts < 10 // Safety limit to prevent infinite loops
|
||
);
|
||
|
||
this.currentNumber = newNumber;
|
||
this.correctAnswer = this.targetSum - this.currentNumber;
|
||
|
||
// Store current question as previous for next iteration
|
||
this.previousQuestion = {
|
||
number: this.currentNumber,
|
||
targetSum: this.targetSum,
|
||
answer: this.correctAnswer
|
||
};
|
||
this.questionStartTime = Date.now();
|
||
|
||
// Update display and clear any pending input
|
||
document.getElementById('challenge-number').textContent = this.currentNumber;
|
||
this.clearAutoSubmitTimeout();
|
||
this.updateAnswerDisplay('');
|
||
document.getElementById('input-feedback').textContent = '';
|
||
document.getElementById('visual-complement').style.display = 'none';
|
||
|
||
// Reset display moving flag for new question
|
||
if (document.querySelector('.route-path')) {
|
||
this.isDisplayMoving = false;
|
||
}
|
||
|
||
// Handle display switching for train mode (static → floating after delay)
|
||
this.handleDisplaySwitching();
|
||
|
||
// Show persistent learning mode feedback
|
||
if (this.difficultyTracker.learningMode && this.totalQuestions % 3 === 0) {
|
||
const pairKey = `${this.currentNumber}_${this.correctAnswer}_${this.targetSum}`;
|
||
const adaptiveFeedback = this.getAdaptiveFeedbackMessage(pairKey, true, 0);
|
||
if (adaptiveFeedback) {
|
||
this.showAdaptiveFeedback(adaptiveFeedback.message, adaptiveFeedback.type);
|
||
}
|
||
}
|
||
|
||
this.startQuestionTimer();
|
||
this.totalQuestions++;
|
||
}
|
||
|
||
startQuestionTimer() {
|
||
const timerFill = document.getElementById('timer-fill');
|
||
const duration = this.getTimerDuration();
|
||
|
||
// Update the timer text to show actual duration
|
||
const timerText = document.getElementById('timer-text');
|
||
if (timerText) {
|
||
timerText.textContent = `${(duration / 1000).toFixed(1)}s`;
|
||
}
|
||
|
||
timerFill.style.width = '100%';
|
||
timerFill.style.backgroundColor = '#4CAF50';
|
||
|
||
let startTime = Date.now();
|
||
this.timerInterval = setInterval(() => {
|
||
if (!this.isGameActive) {
|
||
clearInterval(this.timerInterval);
|
||
return;
|
||
}
|
||
|
||
const elapsed = Date.now() - startTime;
|
||
const remaining = Math.max(0, duration - elapsed);
|
||
const percentage = (remaining / duration) * 100;
|
||
|
||
timerFill.style.width = percentage + '%';
|
||
|
||
// Update countdown text
|
||
if (timerText) {
|
||
timerText.textContent = `${(remaining / 1000).toFixed(1)}s`;
|
||
}
|
||
|
||
if (percentage < 30) {
|
||
timerFill.style.backgroundColor = '#f44336';
|
||
} else if (percentage < 60) {
|
||
timerFill.style.backgroundColor = '#ff9800';
|
||
}
|
||
|
||
if (remaining === 0) {
|
||
clearInterval(this.timerInterval);
|
||
this.timeUp();
|
||
}
|
||
}, 50);
|
||
}
|
||
|
||
getTimerDuration() {
|
||
// Use adaptive difficulty system
|
||
let adaptiveTime = this.getAdaptiveTimeLimit();
|
||
|
||
// Apply game style modifiers to adaptive base
|
||
if (this.style === 'sprint') {
|
||
adaptiveTime = Math.min(adaptiveTime, adaptiveTime * 0.8); // 20% faster for sprint
|
||
} else if (this.style === 'survival') {
|
||
// Survival gets progressively faster based on survival multiplier
|
||
adaptiveTime = adaptiveTime / (this.survivalMultiplier || 1.0);
|
||
}
|
||
|
||
// Apply streak bonus (gets faster with success)
|
||
const streakReduction = Math.min(this.streak * 50, adaptiveTime * 0.3); // Max 30% reduction
|
||
const finalTime = Math.max(1000, adaptiveTime - streakReduction); // Never below 1 second
|
||
|
||
// Show timeout adjustment feedback
|
||
if (this.totalQuestions > 5 && !this.difficultyTracker.learningMode) {
|
||
const originalTime = this.difficultyTracker.baseTimeLimit;
|
||
const timeDiff = Math.abs(finalTime - originalTime) / 1000; // Convert to seconds
|
||
|
||
if (timeDiff > 0.5) { // Only show if significant change
|
||
let timeoutMessage = '';
|
||
let timeoutType = '';
|
||
|
||
if (finalTime < originalTime * 0.8) {
|
||
timeoutMessage = `🔥 You're blazing fast! Reduced time to ${(finalTime/1000).toFixed(1)}s!`;
|
||
timeoutType = 'adapted';
|
||
} else if (finalTime > originalTime * 1.3) {
|
||
timeoutMessage = `🤗 Taking it easy - extended to ${(finalTime/1000).toFixed(1)}s`;
|
||
timeoutType = 'adapted';
|
||
}
|
||
|
||
if (timeoutMessage && this.totalQuestions % 8 === 0) { // Show occasionally
|
||
setTimeout(() => {
|
||
this.showAdaptiveFeedback(timeoutMessage, timeoutType);
|
||
}, 200);
|
||
}
|
||
}
|
||
}
|
||
|
||
// Debug logging for adaptive difficulty
|
||
if (this.totalQuestions % 3 === 0) {
|
||
console.log('🎯 Adaptive Timer:', {
|
||
question: this.totalQuestions,
|
||
baseTime: this.difficultyTracker.baseTimeLimit + 'ms',
|
||
adaptiveTime: adaptiveTime + 'ms',
|
||
finalTime: finalTime + 'ms',
|
||
streak: this.streak,
|
||
difficultyLevel: this.difficultyTracker.difficultyLevel,
|
||
learningMode: this.difficultyTracker.learningMode,
|
||
timeoutSetting: this.timeoutSetting
|
||
});
|
||
}
|
||
|
||
return finalTime;
|
||
}
|
||
|
||
handleKeydown(e) {
|
||
// Only capture keystrokes when game is active and not paused
|
||
if (!this.isGameActive || this.isPaused) return;
|
||
|
||
// Handle number keys (0-9)
|
||
if (e.key >= '0' && e.key <= '9') {
|
||
e.preventDefault();
|
||
const digit = e.key;
|
||
|
||
// Build multi-digit number (max 2 digits for this game)
|
||
let newInput = this.currentInput + digit;
|
||
const number = parseInt(newInput);
|
||
|
||
// Only allow valid answers (0 to 10 for friends of 10, 0 to 5 for friends of 5, max 2 digits)
|
||
const maxValid = Math.max(this.targetSum, 10); // Allow up to 10 for any mode
|
||
if (number <= maxValid && newInput.length <= 2) {
|
||
this.currentInput = newInput;
|
||
this.updateAnswerDisplay(newInput);
|
||
|
||
// Clear any existing auto-submit timeout
|
||
this.clearAutoSubmitTimeout();
|
||
|
||
// Auto-submit after delay
|
||
this.autoSubmitTimeout = setTimeout(() => {
|
||
if (this.currentInput === newInput && this.isGameActive) {
|
||
this.submitAnswer();
|
||
}
|
||
}, newInput === '1' && this.targetSum >= 10 ? 400 : 200);
|
||
}
|
||
}
|
||
|
||
// Handle backspace/delete to remove last digit
|
||
else if (e.key === 'Backspace' || e.key === 'Delete') {
|
||
e.preventDefault();
|
||
|
||
if (this.currentInput.length > 0) {
|
||
this.currentInput = this.currentInput.slice(0, -1);
|
||
this.updateAnswerDisplay(this.currentInput || '');
|
||
this.clearAutoSubmitTimeout();
|
||
} else {
|
||
this.updateAnswerDisplay('');
|
||
}
|
||
}
|
||
|
||
// Handle enter to submit current answer immediately
|
||
else if (e.key === 'Enter' && this.currentInput) {
|
||
e.preventDefault();
|
||
this.clearAutoSubmitTimeout();
|
||
this.submitAnswer();
|
||
}
|
||
}
|
||
|
||
clearAutoSubmitTimeout() {
|
||
if (this.autoSubmitTimeout) {
|
||
clearTimeout(this.autoSubmitTimeout);
|
||
this.autoSubmitTimeout = null;
|
||
}
|
||
}
|
||
|
||
|
||
updateAnswerDisplay(value) {
|
||
const display = document.getElementById('answer-display');
|
||
|
||
if (value === '') {
|
||
display.textContent = '?';
|
||
display.classList.remove('typing');
|
||
display.classList.add('empty');
|
||
this.currentInput = '';
|
||
} else {
|
||
display.textContent = value;
|
||
display.classList.add('typing');
|
||
display.classList.remove('empty');
|
||
}
|
||
}
|
||
|
||
submitAnswer() {
|
||
if (!this.isGameActive) return;
|
||
|
||
clearInterval(this.timerInterval);
|
||
const isCorrect = parseInt(this.currentInput) === this.correctAnswer;
|
||
|
||
if (isCorrect) {
|
||
this.handleCorrectAnswer();
|
||
} else {
|
||
this.handleIncorrectAnswer();
|
||
}
|
||
|
||
setTimeout(() => this.nextQuestion(), 50);
|
||
}
|
||
|
||
handleCorrectAnswer() {
|
||
this.correctAnswers++;
|
||
this.streak++;
|
||
this.bestStreak = Math.max(this.bestStreak, this.streak);
|
||
|
||
let points = 10 + Math.min(this.streak, 10);
|
||
this.score += points;
|
||
|
||
// Show different feedback based on game style
|
||
if (this.style === 'sprint') {
|
||
const config = this.getMomentumConfig();
|
||
this.showFeedback(`🚂 Coal shoveled! +${config.momentumGain}% momentum!`, 'correct');
|
||
} else {
|
||
this.showFeedback('🎉 Correct!', 'correct');
|
||
}
|
||
this.showVisualComplement();
|
||
|
||
// Update race progress
|
||
this.updatePlayerRace();
|
||
|
||
// Trigger particle effects
|
||
const numberDisplay = document.getElementById('current-number');
|
||
if (particleSystem && numberDisplay) {
|
||
if (this.streak > 0 && this.streak % 5 === 0) {
|
||
// Special celebration for every 5th streak
|
||
particleSystem.celebrateStreak(numberDisplay, this.streak);
|
||
} else {
|
||
particleSystem.celebrateSuccess(numberDisplay);
|
||
}
|
||
}
|
||
|
||
// Track performance for adaptive difficulty FIRST
|
||
const responseTime = Date.now() - this.questionStartTime;
|
||
this.trackPerformance(true);
|
||
|
||
// Show adaptive feedback after tracking
|
||
const pairKey = `${this.currentNumber}_${this.correctAnswer}_${this.targetSum}`;
|
||
const adaptiveFeedback = this.getAdaptiveFeedbackMessage(pairKey, true, responseTime);
|
||
if (adaptiveFeedback) {
|
||
setTimeout(() => {
|
||
this.showAdaptiveFeedback(adaptiveFeedback.message, adaptiveFeedback.type);
|
||
}, 600); // Show after the main feedback
|
||
}
|
||
|
||
// Trigger AI commentary
|
||
setTimeout(() => {
|
||
this.triggerAICommentary();
|
||
}, 800);
|
||
|
||
this.updateStats();
|
||
|
||
// Play appropriate sound based on performance
|
||
if (this.streak > 0 && this.streak % 5 === 0) {
|
||
// Epic streak sound for every 5th correct answer
|
||
this.playSound('streak');
|
||
} else if (responseTime < 800) {
|
||
// Whoosh sound for very fast responses (under 800ms)
|
||
this.playSound('whoosh');
|
||
} else if (responseTime < 1200 && this.streak >= 3) {
|
||
// Combo sound for rapid answers while on a streak
|
||
this.playSound('combo');
|
||
} else {
|
||
// Regular correct sound
|
||
this.playSound('correct');
|
||
}
|
||
}
|
||
|
||
handleIncorrectAnswer() {
|
||
this.streak = 0;
|
||
|
||
// Capture values before they might get overwritten, with fallbacks
|
||
const displayNumber = this.currentNumber !== null ? this.currentNumber : '?';
|
||
const displayAnswer = this.correctAnswer !== null ? this.correctAnswer : '?';
|
||
const displaySum = this.targetSum !== null ? this.targetSum : '?';
|
||
|
||
this.showFeedback(`❌ ${displayNumber} + ${displayAnswer} = ${displaySum}`, 'incorrect');
|
||
this.showVisualComplement();
|
||
|
||
// Trigger trip animation for player racer
|
||
const playerRacer = document.getElementById('player-racer');
|
||
if (playerRacer) {
|
||
playerRacer.classList.add('tripped');
|
||
setTimeout(() => {
|
||
playerRacer.classList.remove('tripped');
|
||
}, 1200); // Match animation duration
|
||
}
|
||
|
||
// Track performance for adaptive difficulty FIRST
|
||
const responseTime = Date.now() - this.questionStartTime;
|
||
this.trackPerformance(false);
|
||
|
||
// Handle steam train momentum loss in sprint mode
|
||
if (this.style === 'sprint') {
|
||
this.loseMomentum();
|
||
}
|
||
|
||
// Show adaptive feedback after tracking
|
||
const pairKey = `${displayNumber}_${displayAnswer}_${displaySum}`;
|
||
const adaptiveFeedback = this.getAdaptiveFeedbackMessage(pairKey, false, responseTime);
|
||
if (adaptiveFeedback) {
|
||
setTimeout(() => {
|
||
this.showAdaptiveFeedback(adaptiveFeedback.message, adaptiveFeedback.type);
|
||
}, 800); // Show after the main feedback
|
||
}
|
||
|
||
// Trigger AI commentary
|
||
setTimeout(() => {
|
||
this.triggerAICommentary();
|
||
}, 1000);
|
||
|
||
this.updateStats();
|
||
this.playSound('incorrect');
|
||
}
|
||
|
||
timeUp() {
|
||
this.streak = 0;
|
||
|
||
// Capture values before they might get overwritten, with fallbacks
|
||
const displayNumber = this.currentNumber !== null ? this.currentNumber : '?';
|
||
const displayAnswer = this.correctAnswer !== null ? this.correctAnswer : '?';
|
||
const displaySum = this.targetSum !== null ? this.targetSum : '?';
|
||
|
||
this.showFeedback(`⏰ Time's up! ${displayNumber} + ${displayAnswer} = ${displaySum}`, 'timeout');
|
||
this.showVisualComplement();
|
||
|
||
// Apply timeout penalty: move backwards in race
|
||
if (this.correctAnswers > 0) {
|
||
this.correctAnswers = Math.max(0, this.correctAnswers - 0.5); // Small setback
|
||
}
|
||
|
||
// Trigger backwards movement animation for player racer
|
||
const playerRacer = document.getElementById('player-racer');
|
||
if (playerRacer) {
|
||
playerRacer.classList.add('moving-backwards');
|
||
setTimeout(() => {
|
||
playerRacer.classList.remove('moving-backwards');
|
||
// Update position after animation (no momentum gain for timeout penalty)
|
||
this.updatePlayerRace(false);
|
||
}, 800); // Match animation duration
|
||
}
|
||
|
||
// Track performance for adaptive difficulty (timeout counts as incorrect)
|
||
this.trackPerformance(false);
|
||
|
||
// Handle steam train momentum loss in sprint mode
|
||
if (this.style === 'sprint') {
|
||
this.loseMomentum();
|
||
}
|
||
|
||
this.updateStats();
|
||
this.playSound('timeout');
|
||
setTimeout(() => this.nextQuestion(), 50);
|
||
}
|
||
|
||
showFeedback(message, type) {
|
||
const feedback = document.getElementById('input-feedback');
|
||
feedback.textContent = message;
|
||
feedback.className = `input-feedback ${type}`;
|
||
}
|
||
|
||
showAdaptiveFeedback(message, type) {
|
||
const adaptiveFeedback = document.getElementById('adaptive-feedback');
|
||
adaptiveFeedback.textContent = message;
|
||
adaptiveFeedback.className = `adaptive-feedback ${type}`;
|
||
|
||
// Auto-hide after some time unless it's persistent info
|
||
if (type !== 'learning') {
|
||
setTimeout(() => {
|
||
adaptiveFeedback.style.opacity = '0';
|
||
setTimeout(() => {
|
||
adaptiveFeedback.textContent = '';
|
||
adaptiveFeedback.style.opacity = '0.8';
|
||
}, 400);
|
||
}, 3000);
|
||
}
|
||
}
|
||
|
||
getAdaptiveFeedbackMessage(pairKey, isCorrect, responseTime) {
|
||
const pairData = this.difficultyTracker.pairPerformance.get(pairKey);
|
||
const [num1, num2, sum] = pairKey.split('_').map(Number);
|
||
|
||
// Learning mode messages
|
||
if (this.difficultyTracker.learningMode) {
|
||
const encouragements = [
|
||
"🧠 I'm learning your style! Keep going!",
|
||
"📊 Building your skill profile...",
|
||
"🎯 Every answer helps me understand you better!",
|
||
"🚀 Analyzing your complement superpowers!"
|
||
];
|
||
return {
|
||
message: encouragements[Math.floor(Math.random() * encouragements.length)],
|
||
type: 'learning'
|
||
};
|
||
}
|
||
|
||
// After learning - provide specific feedback
|
||
if (pairData && pairData.attempts >= 3) {
|
||
const accuracy = pairData.correct / pairData.attempts;
|
||
const avgTime = pairData.avgTime;
|
||
|
||
// Struggling pairs (< 60% accuracy)
|
||
if (accuracy < 0.6) {
|
||
const strugglingMessages = [
|
||
`💪 ${num1}+${num2} needs practice - I'm giving you extra time!`,
|
||
`🎯 Working on ${num1}+${num2} - you've got this!`,
|
||
`⏰ Taking it slower with ${num1}+${num2} - no rush!`,
|
||
`🧩 ${num1}+${num2} is getting special attention from me!`
|
||
];
|
||
return {
|
||
message: strugglingMessages[Math.floor(Math.random() * strugglingMessages.length)],
|
||
type: 'struggling'
|
||
};
|
||
}
|
||
|
||
// Mastered pairs (> 85% accuracy and fast)
|
||
if (accuracy > 0.85 && avgTime < 2000) {
|
||
const masteredMessages = [
|
||
`⚡ ${num1}+${num2} = MASTERED! Lightning mode activated!`,
|
||
`🔥 You've conquered ${num1}+${num2} - speeding it up!`,
|
||
`🏆 ${num1}+${num2} expert detected! Challenge mode ON!`,
|
||
`⭐ ${num1}+${num2} is your superpower! Going faster!`
|
||
];
|
||
return {
|
||
message: masteredMessages[Math.floor(Math.random() * masteredMessages.length)],
|
||
type: 'mastered'
|
||
};
|
||
}
|
||
}
|
||
|
||
// Show adaptation when difficulty changes
|
||
if (this.difficultyTracker.consecutiveCorrect >= 3) {
|
||
return {
|
||
message: "🚀 You're on fire! Increasing the challenge!",
|
||
type: 'adapted'
|
||
};
|
||
} else if (this.difficultyTracker.consecutiveIncorrect >= 2) {
|
||
return {
|
||
message: "🤗 Let's slow down a bit - I'm here to help!",
|
||
type: 'adapted'
|
||
};
|
||
}
|
||
|
||
return null;
|
||
}
|
||
|
||
// AI Commentary System - Speech Bubbles
|
||
showAICommentary(racer, message, type = 'default') {
|
||
// Get the speech bubble for this specific racer
|
||
const racerId = racer.id.split('-').pop(); // Extract number from 'ai-racer-1'
|
||
const speechBubble = document.getElementById(`speech-bubble-${racerId}`);
|
||
const bubbleContent = speechBubble?.querySelector('.bubble-content');
|
||
|
||
if (!speechBubble || !bubbleContent) return;
|
||
|
||
// Hide any currently visible bubbles first
|
||
document.querySelectorAll('.speech-bubble.visible').forEach(bubble => {
|
||
if (bubble !== speechBubble) {
|
||
bubble.classList.remove('visible');
|
||
}
|
||
});
|
||
|
||
// Update bubble content
|
||
bubbleContent.textContent = message;
|
||
|
||
// Show this bubble with animation
|
||
speechBubble.classList.add('visible');
|
||
|
||
// Set cooldown for this racer
|
||
racer.lastComment = Date.now();
|
||
racer.commentCooldown = Math.random() * 4000 + 2000; // 2-6 seconds
|
||
|
||
// Auto-hide after some time
|
||
setTimeout(() => {
|
||
speechBubble.classList.remove('visible');
|
||
}, 3500); // Show for 3.5 seconds
|
||
}
|
||
|
||
getAICommentary(racer, context) {
|
||
const now = Date.now();
|
||
|
||
// Check cooldown
|
||
if (now - racer.lastComment < racer.commentCooldown) {
|
||
return null;
|
||
}
|
||
|
||
const playerProgress = (this.correctAnswers / this.raceGoal) * 100;
|
||
const aiProgress = (racer.position / this.raceGoal) * 100;
|
||
|
||
let messages = [];
|
||
|
||
if (racer.personality === 'competitive') {
|
||
// Swift AI - Competitive and cocky
|
||
if (context === 'ahead') {
|
||
messages = [
|
||
"💨 Eat my dust!",
|
||
"🔥 Too slow for me!",
|
||
"⚡ You can't catch me!",
|
||
"🚀 I'm built for speed!",
|
||
"🏃♂️ This is way too easy!"
|
||
];
|
||
} else if (context === 'behind') {
|
||
messages = [
|
||
"😤 Not over yet!",
|
||
"💪 I'm just getting started!",
|
||
"🔥 Watch me catch up to you!",
|
||
"⚡ I'm coming for you!",
|
||
"🏃♂️ This is my comeback!"
|
||
];
|
||
} else if (context === 'adaptive_struggle') {
|
||
messages = [
|
||
"😏 You struggling much?",
|
||
"🤖 Math is easy for me!",
|
||
"⚡ You need to think faster!",
|
||
"🔥 Need me to slow down?"
|
||
];
|
||
} else if (context === 'adaptive_mastery') {
|
||
messages = [
|
||
"😮 You're actually impressive!",
|
||
"🤔 You're getting faster...",
|
||
"😤 Time for me to step it up!",
|
||
"⚡ Not bad for a human!"
|
||
];
|
||
} else if (context === 'player_passed') {
|
||
messages = [
|
||
"😠 No way you just passed me!",
|
||
"🔥 This isn't over!",
|
||
"💨 I'm just getting warmed up!",
|
||
"😤 Your lucky streak won't last!",
|
||
"⚡ I'll be back in front of you soon!"
|
||
];
|
||
} else if (context === 'ai_passed') {
|
||
messages = [
|
||
"💨 See ya later, slowpoke!",
|
||
"😎 Thanks for the warm-up!",
|
||
"🔥 This is how it's done!",
|
||
"⚡ I'll see you at the finish line!",
|
||
"💪 Try to keep up with me!"
|
||
];
|
||
} else if (context === 'lapped') {
|
||
messages = [
|
||
"😡 You just lapped me?! No way!",
|
||
"🤬 This is embarrassing for me!",
|
||
"😤 I'm not going down without a fight!",
|
||
"💢 How did you get so far ahead?!",
|
||
"🔥 Time to show you my real speed!",
|
||
"😠 You won't stay ahead for long!"
|
||
];
|
||
} else if (context === 'desperate_catchup') {
|
||
messages = [
|
||
"🚨 TURBO MODE ACTIVATED! I'm coming for you!",
|
||
"💥 You forced me to unleash my true power!",
|
||
"🔥 NO MORE MR. NICE AI! Time to go all out!",
|
||
"⚡ I'm switching to MAXIMUM OVERDRIVE!",
|
||
"😤 You made me angry - now you'll see what I can do!",
|
||
"🚀 AFTERBURNERS ENGAGED! This isn't over!"
|
||
];
|
||
}
|
||
} else if (racer.personality === 'analytical') {
|
||
// Math Bot - Analytical and encouraging but competitive
|
||
if (context === 'ahead') {
|
||
messages = [
|
||
"📊 My performance is optimal!",
|
||
"🤖 My logic beats your speed!",
|
||
"📈 I have 87% win probability!",
|
||
"⚙️ I'm perfectly calibrated!",
|
||
"🔬 Science prevails over you!"
|
||
];
|
||
} else if (context === 'behind') {
|
||
messages = [
|
||
"🤔 Recalculating my strategy...",
|
||
"📊 You're exceeding my projections!",
|
||
"⚙️ Adjusting my parameters!",
|
||
"🔬 I'm analyzing your technique!",
|
||
"📈 You're a statistical anomaly!"
|
||
];
|
||
} else if (context === 'adaptive_struggle') {
|
||
messages = [
|
||
"📊 I detect inefficiencies in you!",
|
||
"🔬 You should focus on patterns!",
|
||
"⚙️ Use that extra time wisely!",
|
||
"📈 You have room for improvement!"
|
||
];
|
||
} else if (context === 'adaptive_mastery') {
|
||
messages = [
|
||
"🤖 Your optimization is excellent!",
|
||
"📊 Your metrics are impressive!",
|
||
"⚙️ I'm updating my models because of you!",
|
||
"🔬 You have near-AI efficiency!"
|
||
];
|
||
} else if (context === 'player_passed') {
|
||
messages = [
|
||
"🤖 Your strategy is fascinating!",
|
||
"📊 You're an unexpected variable!",
|
||
"⚙️ I'm adjusting my algorithms...",
|
||
"🔬 Your execution is impressive!",
|
||
"📈 I'm recalculating the odds!"
|
||
];
|
||
} else if (context === 'ai_passed') {
|
||
messages = [
|
||
"🤖 My efficiency is optimized!",
|
||
"📊 Just as I calculated!",
|
||
"⚙️ All my systems nominal!",
|
||
"🔬 My logic prevails over you!",
|
||
"📈 I'm at 96% confidence level!"
|
||
];
|
||
} else if (context === 'lapped') {
|
||
messages = [
|
||
"🤖 Error: You have exceeded my projections!",
|
||
"📊 This outcome has 0.3% probability!",
|
||
"⚙️ I need to recalibrate my systems!",
|
||
"🔬 Your performance is... statistically improbable!",
|
||
"📈 My confidence level just dropped to 12%!",
|
||
"🤔 I must analyze your methodology!"
|
||
];
|
||
} else if (context === 'desperate_catchup') {
|
||
messages = [
|
||
"🤖 EMERGENCY PROTOCOL ACTIVATED! Initiating maximum speed!",
|
||
"🚨 CRITICAL GAP DETECTED! Engaging catchup algorithms!",
|
||
"⚙️ OVERCLOCKING MY PROCESSORS! Prepare for rapid acceleration!",
|
||
"📊 PROBABILITY OF FAILURE: UNACCEPTABLE! Switching to turbo mode!",
|
||
"🔬 HYPOTHESIS: You're about to see my true potential!",
|
||
"📈 CONFIDENCE LEVEL: RISING! My comeback protocol is online!"
|
||
];
|
||
}
|
||
}
|
||
|
||
if (messages.length > 0) {
|
||
return messages[Math.floor(Math.random() * messages.length)];
|
||
}
|
||
|
||
return null;
|
||
}
|
||
|
||
triggerAICommentary() {
|
||
// Don't comment too frequently
|
||
if (this.totalQuestions % 4 !== 0) return;
|
||
|
||
const playerProgress = this.correctAnswers;
|
||
|
||
this.aiRacers.forEach(racer => {
|
||
const aiProgress = Math.floor(racer.position);
|
||
let context = '';
|
||
|
||
// Determine context based on positions and adaptive system
|
||
if (aiProgress > playerProgress + 2) {
|
||
context = 'ahead';
|
||
} else if (playerProgress > aiProgress + 5) {
|
||
context = 'desperate_catchup'; // AI is way behind and desperate
|
||
} else if (playerProgress > aiProgress + 2) {
|
||
context = 'behind';
|
||
} else if (this.difficultyTracker.consecutiveIncorrect >= 2) {
|
||
context = 'adaptive_struggle';
|
||
} else if (this.streak >= 5) {
|
||
context = 'adaptive_mastery';
|
||
}
|
||
|
||
if (context) {
|
||
const message = this.getAICommentary(racer, context);
|
||
if (message) {
|
||
this.showAICommentary(racer, message, context);
|
||
|
||
// Play special turbo sound when AI goes desperate
|
||
if (context === 'desperate_catchup') {
|
||
this.playSound('ai_turbo', 0.12);
|
||
}
|
||
|
||
return; // Only one racer comments at a time
|
||
}
|
||
}
|
||
});
|
||
}
|
||
|
||
showVisualComplement() {
|
||
document.getElementById('number-part').textContent = this.currentNumber;
|
||
document.getElementById('complement-part').textContent = this.correctAnswer;
|
||
document.getElementById('sum-part').textContent = this.targetSum;
|
||
document.getElementById('visual-complement').style.display = 'block';
|
||
}
|
||
|
||
updateStats() {
|
||
document.getElementById('complement-score').textContent = this.score;
|
||
document.getElementById('complement-streak').textContent = this.streak;
|
||
|
||
// Update progress display
|
||
const progress = Math.min(this.totalQuestions / 20, 1) * 100;
|
||
document.getElementById('progress-fill').style.width = progress + '%';
|
||
document.getElementById('progress-text').textContent = `${this.correctAnswers}/${this.totalQuestions} Correct`;
|
||
|
||
// Start the continuous game timer if not already running
|
||
if (!this.gameInterval) {
|
||
this.startGameTimer();
|
||
}
|
||
}
|
||
|
||
startGameTimer() {
|
||
// Clear any existing game timer first
|
||
if (this.gameInterval) {
|
||
clearInterval(this.gameInterval);
|
||
}
|
||
|
||
// Start continuous timer that updates every second
|
||
this.gameInterval = setInterval(() => {
|
||
if (!this.isGameActive || this.isPaused) return;
|
||
|
||
const elapsed = Math.floor((Date.now() - this.gameStartTime) / 1000);
|
||
|
||
if (this.style === 'sprint') {
|
||
// For steam train journey - show elapsed time instead of countdown
|
||
document.getElementById('complement-time').textContent = elapsed + 's';
|
||
|
||
// Check for journey completion conditions instead of time limit
|
||
this.checkTrainJourneyCompletion();
|
||
} else {
|
||
// Count-up timer for practice and survival
|
||
document.getElementById('complement-time').textContent = elapsed + 's';
|
||
|
||
// Survival mode: increase difficulty over time
|
||
if (this.style === 'survival' && elapsed > 0 && elapsed % 10 === 0) {
|
||
this.increaseSurvivalDifficulty(elapsed);
|
||
}
|
||
}
|
||
}, 1000);
|
||
}
|
||
|
||
increaseSurvivalDifficulty(elapsed) {
|
||
// Increase difficulty every 10 seconds in survival mode
|
||
const level = Math.floor(elapsed / 10);
|
||
this.survivalMultiplier = 1.0 + (level * 0.15); // 15% faster each level
|
||
|
||
// Speed up AI racers too
|
||
const newSpeedMultiplier = this.speedMultiplier * this.survivalMultiplier;
|
||
this.aiRacers[0].speed = 0.25 * newSpeedMultiplier;
|
||
this.aiRacers[1].speed = 0.15 * newSpeedMultiplier;
|
||
|
||
// Show survival escalation message
|
||
const messages = [
|
||
'🔥 Pressure rising! Questions getting faster!',
|
||
'⚡ Heat turned up! Can you handle it?',
|
||
'🌪️ Intensity increasing! Stay focused!',
|
||
'🚀 Speed boost activated! Don\'t crack!',
|
||
'💥 Maximum pressure! You\'re in the zone!'
|
||
];
|
||
|
||
const message = level < messages.length ? messages[level - 1] : messages[messages.length - 1];
|
||
this.showFeedback(message, 'survival');
|
||
|
||
console.log(`Survival difficulty increased to level ${level}, multiplier: ${this.survivalMultiplier.toFixed(2)}`);
|
||
}
|
||
|
||
pauseRace() {
|
||
this.isGameActive = false;
|
||
this.isPaused = true;
|
||
|
||
// Clear all timers and timeouts
|
||
if (this.timerInterval) {
|
||
clearInterval(this.timerInterval);
|
||
this.timerInterval = null;
|
||
}
|
||
if (this.gameInterval) {
|
||
clearInterval(this.gameInterval);
|
||
this.gameInterval = null;
|
||
}
|
||
this.clearAutoSubmitTimeout();
|
||
|
||
this.showFeedback('⏸️ Paused - Click End to finish', 'pause');
|
||
}
|
||
|
||
endRace(customMessage = null) {
|
||
this.isGameActive = false;
|
||
|
||
// Stop all sounds immediately
|
||
this.stopAllSounds();
|
||
|
||
// Clear all timers and timeouts
|
||
if (this.timerInterval) {
|
||
clearInterval(this.timerInterval);
|
||
this.timerInterval = null;
|
||
}
|
||
if (this.gameInterval) {
|
||
clearInterval(this.gameInterval);
|
||
this.gameInterval = null;
|
||
}
|
||
if (this.momentumDecayInterval) {
|
||
clearInterval(this.momentumDecayInterval);
|
||
this.momentumDecayInterval = null;
|
||
}
|
||
if (this.trainMovementInterval) {
|
||
clearInterval(this.trainMovementInterval);
|
||
this.trainMovementInterval = null;
|
||
}
|
||
this.clearAutoSubmitTimeout();
|
||
|
||
const accuracy = this.totalQuestions > 0 ? (this.correctAnswers / this.totalQuestions) * 100 : 0;
|
||
const totalTime = (Date.now() - this.gameStartTime) / 1000;
|
||
|
||
// Stop race and show intro content when game ends
|
||
this.stopRace();
|
||
document.querySelector('.complement-intro').style.display = 'block';
|
||
|
||
this.showResults({
|
||
score: this.score,
|
||
correct: this.correctAnswers,
|
||
total: this.totalQuestions,
|
||
accuracy: accuracy,
|
||
bestStreak: this.bestStreak,
|
||
totalTime: totalTime,
|
||
mode: this.mode,
|
||
raceWinner: this.raceWinner || null,
|
||
winningAI: this.winningAI || null
|
||
});
|
||
|
||
this.resetGame();
|
||
}
|
||
|
||
showResults(results) {
|
||
const modal = document.getElementById('complement-score-modal');
|
||
const body = document.getElementById('complement-score-modal-body');
|
||
|
||
// Calculate race performance score (combination of speed and accuracy)
|
||
const raceResult = this.calculateRaceResult(results);
|
||
|
||
const modeNames = { 'friends5': 'Friends of 5', 'friends10': 'Friends of 10', 'mixed': 'Mixed Challenge' };
|
||
|
||
body.innerHTML = `
|
||
<div class="race-results-summary">
|
||
<div class="race-podium">
|
||
<div class="medal-display">
|
||
<div class="medal-icon">${raceResult.medal}</div>
|
||
<div class="medal-rank">${raceResult.rank}</div>
|
||
<div class="medal-title">${raceResult.title}</div>
|
||
</div>
|
||
<div class="race-performance">
|
||
<div class="perf-metric">
|
||
<span class="metric-label">Speed Rating</span>
|
||
<span class="metric-value">${raceResult.speedRating}/100</span>
|
||
</div>
|
||
<div class="perf-metric">
|
||
<span class="metric-label">Accuracy</span>
|
||
<span class="metric-value">${results.accuracy.toFixed(1)}%</span>
|
||
</div>
|
||
<div class="perf-metric">
|
||
<span class="metric-label">Race Score</span>
|
||
<span class="metric-value">${raceResult.raceScore}/100</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="race-statistics">
|
||
<div class="stat-row">
|
||
<span class="stat-label">🏁 Questions Completed:</span>
|
||
<span class="stat-value">${results.correct}/${results.total}</span>
|
||
</div>
|
||
<div class="stat-row">
|
||
<span class="stat-label">🔥 Best Streak:</span>
|
||
<span class="stat-value">${results.bestStreak}</span>
|
||
</div>
|
||
<div class="stat-row">
|
||
<span class="stat-label">⏱️ Total Time:</span>
|
||
<span class="stat-value">${results.totalTime.toFixed(1)}s</span>
|
||
</div>
|
||
<div class="stat-row">
|
||
<span class="stat-label">📈 Points Earned:</span>
|
||
<span class="stat-value">${results.score}</span>
|
||
</div>
|
||
<div class="stat-row">
|
||
<span class="stat-label">🎯 Challenge:</span>
|
||
<span class="stat-value">${modeNames[results.mode]}</span>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="race-feedback">
|
||
<p>${raceResult.feedback}</p>
|
||
</div>
|
||
</div>
|
||
`;
|
||
|
||
modal.style.display = 'block';
|
||
}
|
||
|
||
calculateRaceResult(results) {
|
||
// Calculate speed rating (questions per minute)
|
||
const questionsPerMinute = (results.correct / results.totalTime) * 60;
|
||
const maxQPM = 30; // Maximum expected questions per minute for normalization
|
||
const speedRating = Math.min(100, Math.round((questionsPerMinute / maxQPM) * 100));
|
||
|
||
// Calculate overall race score (weighted combination) for performance tracking
|
||
const raceScore = Math.round((results.accuracy * 0.7) + (speedRating * 0.3));
|
||
|
||
// Determine medal and rank based on ACTUAL RACE RESULTS first
|
||
let medal, rank, title, feedback;
|
||
|
||
if (results.raceWinner === 'player') {
|
||
// Player won the race - always gets gold!
|
||
medal = '🥇';
|
||
rank = '1st Place';
|
||
title = 'GOLD MEDAL - RACE WINNER!';
|
||
|
||
if (results.accuracy >= 90) {
|
||
feedback = 'PERFECT VICTORY! You won with outstanding accuracy. You\'re a true complement champion!';
|
||
} else if (results.accuracy >= 75) {
|
||
feedback = 'RACE WINNER! Great job beating the AI racers. Your speed gave you the edge!';
|
||
} else {
|
||
feedback = 'VICTORY BY SPEED! You won the race even with some mistakes. Speed can overcome accuracy!';
|
||
}
|
||
} else if (results.raceWinner === 'ai') {
|
||
// AI won the race, player gets silver for effort
|
||
medal = '🥈';
|
||
rank = '2nd Place';
|
||
title = 'SILVER MEDAL';
|
||
|
||
if (results.accuracy >= 85) {
|
||
feedback = 'So close! Your accuracy was excellent, but the AI was just a bit faster this time.';
|
||
} else if (results.accuracy >= 70) {
|
||
feedback = 'Good race! Work on speed to beat those AI racers next time.';
|
||
} else {
|
||
feedback = 'The AI won this round, but you\'re improving! Focus on accuracy first, then speed.';
|
||
}
|
||
} else {
|
||
// For sprint/survival modes - determine position based on who's ahead at end
|
||
const playerPosition = this.correctAnswers;
|
||
const aiPositions = this.aiRacers.map(ai => ai.position).sort((a, b) => b - a);
|
||
|
||
let playerRank = 1;
|
||
aiPositions.forEach(aiPos => {
|
||
if (aiPos > playerPosition) {
|
||
playerRank++;
|
||
}
|
||
});
|
||
|
||
if (playerRank === 1) {
|
||
medal = '🥇';
|
||
rank = '1st Place';
|
||
title = 'GOLD MEDAL - RACE LEADER!';
|
||
feedback = `RACE LEADER! You were ahead of both AI racers with ${playerPosition} correct answers. Outstanding performance!`;
|
||
} else if (playerRank === 2) {
|
||
const leadingAI = Math.max(...aiPositions);
|
||
medal = '🥈';
|
||
rank = '2nd Place';
|
||
title = 'SILVER MEDAL';
|
||
feedback = `STRONG SECOND! You finished just behind the leading AI racer (you: ${playerPosition}, AI: ${leadingAI}). Great effort!`;
|
||
} else if (playerRank === 3) {
|
||
medal = '🥉';
|
||
rank = '3rd Place';
|
||
title = 'BRONZE MEDAL';
|
||
feedback = `BRONZE FINISH! Both AI racers got ahead, but you completed the race with ${playerPosition} correct answers. Keep practicing!`;
|
||
} else {
|
||
medal = '🏅';
|
||
rank = 'Participant';
|
||
title = 'COMPLETION MEDAL';
|
||
feedback = 'Thanks for playing! Focus on accuracy first, then work on building speed.';
|
||
}
|
||
}
|
||
|
||
return {
|
||
medal,
|
||
rank,
|
||
title,
|
||
speedRating,
|
||
raceScore,
|
||
feedback
|
||
};
|
||
}
|
||
|
||
resetGame() {
|
||
// Show intro and controls, hide game
|
||
document.querySelector('.complement-intro').style.display = 'block';
|
||
document.querySelector('.complement-controls').style.display = 'block';
|
||
document.getElementById('complement-game').style.display = 'none';
|
||
document.getElementById('complement-header').style.display = 'none';
|
||
document.getElementById('visual-complement').style.display = 'none';
|
||
}
|
||
|
||
// Race Management Methods
|
||
initializeRace() {
|
||
// Set race goal and mechanics based on style
|
||
if (this.style === 'practice') {
|
||
this.raceGoal = 20; // Race to 20 correct answers
|
||
this.timeLimit = null; // No time limit
|
||
this.speedMultiplier = 0.7; // Moderate AI speed
|
||
} else if (this.style === 'sprint') {
|
||
this.raceGoal = 999; // No finish line - count as many as possible
|
||
this.timeLimit = 60; // 60-second time limit
|
||
this.speedMultiplier = 0.9; // Aggressive AI speed
|
||
} else if (this.style === 'survival') {
|
||
this.raceGoal = 999; // No finish line - survive as long as possible
|
||
this.timeLimit = null; // No time limit
|
||
this.speedMultiplier = 0.5; // Start slower, will increase over time
|
||
this.survivalMultiplier = 1.0; // Difficulty multiplier for survival
|
||
}
|
||
|
||
// Set appropriate goal display and labels for each mode
|
||
const raceGoalElement = document.getElementById('race-goal');
|
||
|
||
if (this.style === 'practice') {
|
||
raceGoalElement.textContent = this.raceGoal; // Show "20"
|
||
raceGoalElement.setAttribute('title', 'Race to this many correct answers');
|
||
} else if (this.style === 'sprint') {
|
||
raceGoalElement.textContent = `${this.timeLimit}s`; // Show "60s"
|
||
raceGoalElement.setAttribute('title', 'Time-based challenge! Get as many as possible before time runs out');
|
||
raceGoalElement.style.color = '#ffd700'; // Gold color for time display
|
||
raceGoalElement.style.fontWeight = 'bold';
|
||
} else if (this.style === 'survival') {
|
||
raceGoalElement.textContent = 'Lap 1'; // Show current lap
|
||
raceGoalElement.setAttribute('title', 'Survive as long as possible!');
|
||
}
|
||
document.getElementById('race-correct').textContent = '0';
|
||
|
||
// Adjust AI speeds based on style
|
||
this.aiRacers[0].speed = 0.25 * this.speedMultiplier; // Swift AI
|
||
this.aiRacers[1].speed = 0.15 * this.speedMultiplier; // Math Bot
|
||
|
||
console.log('Race initialized:', this.style, 'Goal:', this.raceGoal, 'Time limit:', this.timeLimit, 'AI speeds:', this.aiRacers.map(ai => ai.speed));
|
||
|
||
// Show/hide finish line based on race mode and update track appearance
|
||
const finishLine = document.getElementById('finish-line');
|
||
const finishZone = document.getElementById('finish-zone');
|
||
const raceTrackSection = document.querySelector('.race-track-section');
|
||
|
||
if (this.style === 'practice') {
|
||
// Show finish line for practice mode (endurance test)
|
||
if (finishLine) finishLine.style.display = 'block';
|
||
if (finishZone) finishZone.style.display = 'block';
|
||
if (raceTrackSection) raceTrackSection.classList.remove('infinite-mode');
|
||
} else if (this.style === 'sprint') {
|
||
// Hide finish line for sprint mode - it's time-based, not distance-based
|
||
if (finishLine) finishLine.style.display = 'none';
|
||
if (finishZone) finishZone.style.display = 'none';
|
||
if (raceTrackSection) raceTrackSection.classList.add('infinite-mode');
|
||
} else {
|
||
// Hide finish line for survival mode
|
||
if (finishLine) finishLine.style.display = 'none';
|
||
if (finishZone) finishZone.style.display = 'none';
|
||
if (raceTrackSection) raceTrackSection.classList.add('infinite-mode');
|
||
}
|
||
|
||
// Reset all racers to starting position
|
||
const playerRacer = document.getElementById('player-racer');
|
||
const aiRacer1 = document.getElementById('ai-racer-1');
|
||
const aiRacer2 = document.getElementById('ai-racer-2');
|
||
|
||
if (this.style === 'survival') {
|
||
// Position racers on circular track (all at starting position)
|
||
if (playerRacer) this.updateCircularPosition(playerRacer, 0);
|
||
if (aiRacer1) this.updateCircularPosition(aiRacer1, 0);
|
||
if (aiRacer2) this.updateCircularPosition(aiRacer2, 0);
|
||
} else {
|
||
// Position racers on linear track
|
||
if (playerRacer) playerRacer.style.left = '2%';
|
||
if (aiRacer1) aiRacer1.style.left = '2%';
|
||
if (aiRacer2) aiRacer2.style.left = '2%';
|
||
}
|
||
|
||
// Reset AI racers
|
||
this.aiRacers.forEach(ai => {
|
||
ai.position = 2;
|
||
});
|
||
|
||
// Reset lap tracking for circular mode
|
||
if (this.style === 'survival') {
|
||
this.playerLap = 0;
|
||
this.aiLaps.clear();
|
||
this.lapCelebrationCooldown.clear();
|
||
}
|
||
|
||
// Generate dynamic track for train variant
|
||
if (this.style === 'sprint') {
|
||
this.generateDynamicTrack();
|
||
}
|
||
|
||
// Start AI movement
|
||
this.startAIRacers();
|
||
}
|
||
|
||
generateDynamicTrack() {
|
||
// Generate a dynamic curved railroad track with interesting topology
|
||
const svg = document.querySelector('.route-path svg');
|
||
if (!svg) return;
|
||
|
||
// Clear existing track elements
|
||
svg.innerHTML = '';
|
||
|
||
// Generate track waypoints with some randomness for variety
|
||
const waypoints = this.generateTrackWaypoints();
|
||
|
||
// Generate smooth curved path through waypoints
|
||
const pathData = this.generateSmoothPath(waypoints);
|
||
|
||
// Create main reference path for train positioning
|
||
const referencePath = document.createElementNS('http://www.w3.org/2000/svg', 'path');
|
||
referencePath.setAttribute('class', 'train-route');
|
||
referencePath.setAttribute('d', pathData);
|
||
referencePath.setAttribute('fill', 'none');
|
||
referencePath.setAttribute('stroke', 'transparent');
|
||
referencePath.setAttribute('stroke-width', '2');
|
||
|
||
// Create railroad bed/ballast
|
||
const ballast = document.createElementNS('http://www.w3.org/2000/svg', 'path');
|
||
ballast.setAttribute('class', 'railroad-ballast');
|
||
ballast.setAttribute('d', pathData);
|
||
ballast.setAttribute('fill', 'none');
|
||
ballast.setAttribute('stroke', '#8B7355');
|
||
ballast.setAttribute('stroke-width', '24');
|
||
ballast.setAttribute('stroke-linecap', 'round');
|
||
|
||
// Create containers for ties and rails
|
||
const tiesGroup = document.createElementNS('http://www.w3.org/2000/svg', 'g');
|
||
tiesGroup.setAttribute('class', 'railroad-ties');
|
||
|
||
const railsGroup = document.createElementNS('http://www.w3.org/2000/svg', 'g');
|
||
railsGroup.setAttribute('class', 'railroad-rails');
|
||
|
||
// Add elements to SVG in correct order
|
||
svg.appendChild(ballast);
|
||
svg.appendChild(tiesGroup);
|
||
svg.appendChild(railsGroup);
|
||
svg.appendChild(referencePath);
|
||
|
||
// Generate ties and rails properly
|
||
this.generateRailroadTiesAndRails();
|
||
|
||
console.log('🛤️ Generated dynamic curved railroad track with proper rail construction');
|
||
}
|
||
|
||
generateTrackWaypoints() {
|
||
// Create interesting waypoints for a scenic railroad journey
|
||
const waypoints = [
|
||
{ x: 50, y: 300 }, // Start at depot
|
||
{ x: 150, y: 220 }, // Climb into hills
|
||
{ x: 280, y: 180 }, // Mountain pass
|
||
{ x: 420, y: 240 }, // Descent to valley
|
||
{ x: 550, y: 160 }, // Bridge over canyon
|
||
{ x: 680, y: 200 }, // Rolling hills
|
||
{ x: 750, y: 280 } // Arrive at destination
|
||
];
|
||
|
||
// Add some controlled randomness for variety (but keep it playable)
|
||
return waypoints.map((point, index) => {
|
||
if (index === 0 || index === waypoints.length - 1) {
|
||
return point; // Keep start/end points fixed
|
||
}
|
||
|
||
return {
|
||
x: point.x + (Math.random() - 0.5) * 30,
|
||
y: point.y + (Math.random() - 0.5) * 40
|
||
};
|
||
});
|
||
}
|
||
|
||
generateSmoothPath(waypoints) {
|
||
// Generate smooth cubic bezier curves through waypoints
|
||
if (waypoints.length < 2) return '';
|
||
|
||
let pathData = `M ${waypoints[0].x} ${waypoints[0].y}`;
|
||
|
||
for (let i = 1; i < waypoints.length; i++) {
|
||
const current = waypoints[i];
|
||
const previous = waypoints[i - 1];
|
||
|
||
// Calculate control points for smooth curves
|
||
const dx = current.x - previous.x;
|
||
const dy = current.y - previous.y;
|
||
|
||
const cp1x = previous.x + dx * 0.3;
|
||
const cp1y = previous.y + dy * 0.2;
|
||
const cp2x = current.x - dx * 0.3;
|
||
const cp2y = current.y - dy * 0.2;
|
||
|
||
pathData += ` C ${cp1x} ${cp1y}, ${cp2x} ${cp2y}, ${current.x} ${current.y}`;
|
||
}
|
||
|
||
return pathData;
|
||
}
|
||
|
||
offsetPath(pathData, offset) {
|
||
// Simplified path offset - in a full implementation would use proper path offsetting
|
||
// For now, just offset the waypoints and regenerate
|
||
const points = this.extractPointsFromPath(pathData);
|
||
const offsetPoints = points.map(point => ({
|
||
x: point.x + (Math.random() - 0.5) * 2, // Slight randomness
|
||
y: point.y + offset
|
||
}));
|
||
|
||
return this.generateSmoothPath(offsetPoints);
|
||
}
|
||
|
||
extractPointsFromPath(pathData) {
|
||
// Extract key points from path data (simplified)
|
||
const matches = pathData.match(/(\d+(?:\.\d+)?)\s+(\d+(?:\.\d+)?)/g);
|
||
if (!matches) return [];
|
||
|
||
return matches.map(match => {
|
||
const [x, y] = match.split(/\s+/).map(Number);
|
||
return { x, y };
|
||
});
|
||
}
|
||
|
||
generateRailroadTiesAndRails() {
|
||
// Generate railroad properly: ties first, then rails connecting the tie ends
|
||
const tiesContainer = document.querySelector('.railroad-ties');
|
||
const railsContainer = document.querySelector('.railroad-rails');
|
||
const routePath = document.querySelector('.train-route');
|
||
|
||
if (!tiesContainer || !railsContainer || !routePath) return;
|
||
|
||
// Clear existing elements
|
||
tiesContainer.innerHTML = '';
|
||
railsContainer.innerHTML = '';
|
||
|
||
const pathLength = routePath.getTotalLength();
|
||
const tieSpacing = 12; // Distance between ties
|
||
const tieCount = Math.floor(pathLength / tieSpacing);
|
||
const gaugeWidth = 15; // Standard gauge (tie extends 15px each side)
|
||
|
||
const leftRailPoints = [];
|
||
const rightRailPoints = [];
|
||
|
||
// Generate ties and collect rail points
|
||
for (let i = 0; i < tieCount; i++) {
|
||
const distance = i * tieSpacing;
|
||
const point = routePath.getPointAtLength(distance);
|
||
|
||
// Calculate perpendicular angle for tie orientation
|
||
const nextDistance = Math.min(distance + 2, pathLength);
|
||
const nextPoint = routePath.getPointAtLength(nextDistance);
|
||
const angle = Math.atan2(nextPoint.y - point.y, nextPoint.x - point.x);
|
||
const perpAngle = angle + Math.PI / 2;
|
||
|
||
// Calculate tie end points
|
||
const leftX = point.x + Math.cos(perpAngle) * gaugeWidth;
|
||
const leftY = point.y + Math.sin(perpAngle) * gaugeWidth;
|
||
const rightX = point.x - Math.cos(perpAngle) * gaugeWidth;
|
||
const rightY = point.y - Math.sin(perpAngle) * gaugeWidth;
|
||
|
||
// Create railroad tie
|
||
const tie = document.createElementNS('http://www.w3.org/2000/svg', 'line');
|
||
tie.setAttribute('x1', leftX);
|
||
tie.setAttribute('y1', leftY);
|
||
tie.setAttribute('x2', rightX);
|
||
tie.setAttribute('y2', rightY);
|
||
tie.setAttribute('stroke', '#654321');
|
||
tie.setAttribute('stroke-width', '3');
|
||
tie.setAttribute('stroke-linecap', 'round');
|
||
tie.setAttribute('opacity', '0.8');
|
||
|
||
tiesContainer.appendChild(tie);
|
||
|
||
// Collect points for rails
|
||
leftRailPoints.push(`${leftX},${leftY}`);
|
||
rightRailPoints.push(`${rightX},${rightY}`);
|
||
}
|
||
|
||
// Create left rail by connecting left ends of ties
|
||
if (leftRailPoints.length > 1) {
|
||
const leftRail = document.createElementNS('http://www.w3.org/2000/svg', 'polyline');
|
||
leftRail.setAttribute('points', leftRailPoints.join(' '));
|
||
leftRail.setAttribute('fill', 'none');
|
||
leftRail.setAttribute('stroke', '#C0C0C0');
|
||
leftRail.setAttribute('stroke-width', '3');
|
||
leftRail.setAttribute('stroke-linecap', 'round');
|
||
railsContainer.appendChild(leftRail);
|
||
}
|
||
|
||
// Create right rail by connecting right ends of ties
|
||
if (rightRailPoints.length > 1) {
|
||
const rightRail = document.createElementNS('http://www.w3.org/2000/svg', 'polyline');
|
||
rightRail.setAttribute('points', rightRailPoints.join(' '));
|
||
rightRail.setAttribute('fill', 'none');
|
||
rightRail.setAttribute('stroke', '#C0C0C0');
|
||
rightRail.setAttribute('stroke-width', '3');
|
||
rightRail.setAttribute('stroke-linecap', 'round');
|
||
railsContainer.appendChild(rightRail);
|
||
}
|
||
|
||
console.log(`🛤️ Generated ${tieCount} ties and connected rails properly`);
|
||
}
|
||
|
||
updatePlayerRace(isCorrectAnswer = true) {
|
||
if (this.style === 'sprint') {
|
||
// Update steam train journey instead of race track
|
||
if (isCorrectAnswer) {
|
||
this.shovelsCoal(); // Only shovel coal on correct answers
|
||
}
|
||
this.updateTrainPosition();
|
||
return;
|
||
}
|
||
|
||
let visualProgress;
|
||
|
||
if (this.style === 'practice') {
|
||
// Traditional race to finish line
|
||
const progress = (this.correctAnswers / this.raceGoal) * 100;
|
||
visualProgress = Math.min(progress, 100);
|
||
} else {
|
||
// Sprint and survival: visual progress based on score relative to a reasonable target
|
||
if (this.style === 'survival') {
|
||
// For survival mode, let players continue moving forward with cycling
|
||
const visualTarget = 50; // Larger target for survival mode
|
||
const progress = (this.correctAnswers / visualTarget) * 100;
|
||
visualProgress = Math.min(progress, 95); // Allow almost to the end
|
||
} else {
|
||
// Sprint mode: visual progress based on score relative to a reasonable target
|
||
const visualTarget = 30;
|
||
const progress = (this.correctAnswers / visualTarget) * 100;
|
||
visualProgress = Math.min(progress, 95); // Cap at 95% so no one "wins" visually
|
||
}
|
||
}
|
||
|
||
const playerRacer = document.getElementById('player-racer');
|
||
const progressDisplay = document.getElementById('player-progress');
|
||
|
||
if (playerRacer) {
|
||
// Always move players based on their progress
|
||
playerRacer.style.left = visualProgress + '%';
|
||
|
||
// Add celebration animation for good progress
|
||
if (this.correctAnswers % 5 === 0 && this.correctAnswers > 0) {
|
||
playerRacer.classList.add('celebrating');
|
||
setTimeout(() => playerRacer.classList.remove('celebrating'), 600);
|
||
|
||
// Play milestone sound effect
|
||
this.playSound('milestone', 0.18);
|
||
}
|
||
|
||
// In survival mode, use circular positioning
|
||
if (this.style === 'survival') {
|
||
const lapInfo = this.updateCircularPosition(playerRacer, this.correctAnswers);
|
||
|
||
// Check if player completed a new lap
|
||
if (lapInfo.lap > this.playerLap) {
|
||
this.playerLap = lapInfo.lap;
|
||
this.checkForLappingCelebration();
|
||
|
||
// Update lap display in goal area
|
||
document.getElementById('race-goal').textContent = `Lap ${this.playerLap + 1}`; // Show next lap
|
||
}
|
||
}
|
||
}
|
||
|
||
if (progressDisplay) {
|
||
progressDisplay.textContent = this.correctAnswers;
|
||
}
|
||
|
||
document.getElementById('race-correct').textContent = this.correctAnswers;
|
||
|
||
// Check for passing events (player passing or getting passed by AI)
|
||
this.checkForPassingEvents();
|
||
|
||
// Update previous position for next comparison
|
||
this.previousPlayerProgress = this.correctAnswers;
|
||
|
||
// Check if player wins - only for practice mode with finish line
|
||
if (this.style === 'practice' && this.correctAnswers >= this.raceGoal && this.isGameActive) {
|
||
console.log(`🏁 PLAYER WINS! Correct: ${this.correctAnswers}, Goal: ${this.raceGoal}, Visual: ${visualProgress}%`);
|
||
this.handleRaceWin('player');
|
||
} else if (this.style === 'practice') {
|
||
console.log(`🏃 Player progress: ${this.correctAnswers}/${this.raceGoal} (Visual: ${visualProgress.toFixed(1)}%)`);
|
||
}
|
||
}
|
||
|
||
resetInfiniteTrack() {
|
||
// Prevent multiple resets at once
|
||
if (this.isResetting) return;
|
||
this.isResetting = true;
|
||
|
||
console.log('🔄 Resetting infinite track cycle...');
|
||
|
||
const raceTrackContainer = document.querySelector('.race-track-container');
|
||
const allRacers = document.querySelectorAll('.racer');
|
||
|
||
if (!raceTrackContainer) return;
|
||
|
||
// Add smooth scrolling animation
|
||
raceTrackContainer.classList.add('infinite-runner');
|
||
|
||
// After a brief scroll animation, reset all positions
|
||
setTimeout(() => {
|
||
// Reset all racer positions back by 40% to continue the journey
|
||
const resetAmount = 40;
|
||
|
||
allRacers.forEach((racer, index) => {
|
||
const currentLeft = parseFloat(racer.style.left || '0');
|
||
const newLeft = Math.max(10, currentLeft - resetAmount); // Don't go below 10%
|
||
racer.style.left = newLeft + '%';
|
||
console.log(`🏃 Racer ${index} reset: ${currentLeft}% → ${newLeft}%`);
|
||
});
|
||
|
||
// Also adjust AI tracking positions
|
||
this.aiRacers.forEach(ai => {
|
||
const resetInUnits = (resetAmount / 100) * 50; // Convert % to position units
|
||
ai.position = Math.max(2, ai.position - resetInUnits);
|
||
});
|
||
|
||
// Remove animation and reset flag
|
||
raceTrackContainer.classList.remove('infinite-runner');
|
||
this.isResetting = false;
|
||
|
||
console.log('✅ Infinite track reset complete - journey continues!');
|
||
}, 1500); // Short animation duration
|
||
}
|
||
|
||
checkForPassingEvents() {
|
||
const currentPlayerProgress = this.correctAnswers;
|
||
const previousPlayerProgress = this.previousPlayerProgress;
|
||
|
||
this.aiRacers.forEach(ai => {
|
||
const currentAIProgress = ai.position;
|
||
const previousAIProgress = ai.previousPosition;
|
||
|
||
// Add tolerance for floating point comparison and ensure meaningful position changes
|
||
const tolerance = 0.1;
|
||
const playerProgressed = currentPlayerProgress > previousPlayerProgress;
|
||
const aiProgressed = currentAIProgress > previousAIProgress + tolerance;
|
||
|
||
// Check if player just passed AI (player progressed and now ahead)
|
||
if (playerProgressed && previousPlayerProgress <= previousAIProgress && currentPlayerProgress > currentAIProgress) {
|
||
// Player passed AI - AI should react with "player_passed" context
|
||
const message = this.getAICommentary(ai, 'player_passed');
|
||
if (message) {
|
||
setTimeout(() => {
|
||
this.showAICommentary(ai, message, 'player_passed');
|
||
}, Math.random() * 500 + 300); // Slight delay with randomness
|
||
}
|
||
}
|
||
|
||
// Check if AI just passed player (AI progressed and now ahead)
|
||
if (aiProgressed && previousAIProgress <= previousPlayerProgress && currentAIProgress > currentPlayerProgress) {
|
||
// AI passed player - AI should react with "ai_passed" context
|
||
const message = this.getAICommentary(ai, 'ai_passed');
|
||
if (message) {
|
||
setTimeout(() => {
|
||
this.showAICommentary(ai, message, 'ai_passed');
|
||
}, Math.random() * 500 + 300); // Slight delay with randomness
|
||
}
|
||
}
|
||
});
|
||
}
|
||
|
||
updateCircularPosition(racer, progress) {
|
||
// Get track size dynamically to support responsive design
|
||
const track = document.querySelector('.race-track.circular');
|
||
const trackSize = track ? track.offsetWidth : 400;
|
||
const trackRadius = (trackSize / 2) - 20; // Track radius minus racer size
|
||
|
||
// Each point of progress moves the player around the track
|
||
// Complete one full circle every 50 progress points
|
||
const progressPerLap = 50;
|
||
const currentLap = Math.floor(progress / progressPerLap);
|
||
const totalRotation = (progress / progressPerLap) * 360;
|
||
const angleInRadians = (totalRotation * Math.PI) / 180;
|
||
|
||
// Calculate position on circle (centered at track center)
|
||
const x = trackRadius * Math.cos(angleInRadians - Math.PI/2); // Start at top
|
||
const y = trackRadius * Math.sin(angleInRadians - Math.PI/2);
|
||
|
||
// Position racer relative to track center
|
||
const trackCenter = trackSize / 2;
|
||
racer.style.left = (trackCenter + x) + 'px';
|
||
racer.style.top = (trackCenter + y) + 'px';
|
||
|
||
// Add slight rotation to racer to face direction of travel
|
||
racer.style.transform = `rotate(${totalRotation}deg)`;
|
||
|
||
// Set counter-rotation for speech bubbles to keep them upright
|
||
racer.style.setProperty('--counter-rotation', `${-totalRotation}deg`);
|
||
|
||
// Set rotation variable for circular bounce animation
|
||
racer.style.setProperty('--racer-rotation', `${totalRotation}deg`);
|
||
|
||
// Return lap information for celebration detection
|
||
return {
|
||
lap: currentLap,
|
||
progress: progress,
|
||
position: { x: 200 + x, y: 200 + y }
|
||
};
|
||
}
|
||
|
||
checkForLappingCelebration() {
|
||
// Check if player has lapped any AI opponents
|
||
this.aiRacers.forEach(ai => {
|
||
const aiLap = this.aiLaps.get(ai.id) || 0;
|
||
const lapDifference = this.playerLap - aiLap;
|
||
|
||
// If player is a full lap ahead and we haven't celebrated this yet
|
||
if (lapDifference >= 1) {
|
||
const celebrationKey = `${ai.id}_lap_${this.playerLap}`;
|
||
|
||
if (!this.lapCelebrationCooldown.has(celebrationKey)) {
|
||
this.lapCelebrationCooldown.add(celebrationKey);
|
||
|
||
// Trigger celebration effects
|
||
this.celebrateLapping(ai.name);
|
||
|
||
// Show AI commentary about being lapped
|
||
const message = this.getAICommentary(ai, 'lapped');
|
||
if (message) {
|
||
setTimeout(() => {
|
||
this.showAICommentary(ai, message, 'lapped');
|
||
}, 800); // Delay to let celebration play
|
||
}
|
||
|
||
console.log(`🎉 Player lapped ${ai.name}! Player lap: ${this.playerLap}, AI lap: ${aiLap}`);
|
||
}
|
||
}
|
||
});
|
||
}
|
||
|
||
celebrateLapping(aiName) {
|
||
// Visual celebration for lapping an opponent
|
||
const playerRacer = document.getElementById('player-racer');
|
||
if (playerRacer) {
|
||
// Add special lapping celebration class
|
||
playerRacer.classList.add('lapping-celebration');
|
||
setTimeout(() => playerRacer.classList.remove('lapping-celebration'), 1500);
|
||
|
||
// Trigger particle effects
|
||
if (window.particleSystem) {
|
||
window.particleSystem.celebrateMajor(playerRacer);
|
||
}
|
||
|
||
// Show celebration message
|
||
this.showCelebrationMessage(`🏁 LAPPED ${aiName.toUpperCase()}! 🏁`);
|
||
|
||
// Play celebration sound (if available)
|
||
this.playSound('lap_celebration', 0.6);
|
||
}
|
||
}
|
||
|
||
showCelebrationMessage(message) {
|
||
// Create floating celebration message
|
||
const celebration = document.createElement('div');
|
||
celebration.className = 'lap-celebration-message';
|
||
celebration.textContent = message;
|
||
celebration.style.cssText = `
|
||
position: fixed;
|
||
top: 20%;
|
||
left: 50%;
|
||
transform: translateX(-50%);
|
||
background: linear-gradient(135deg, #ffd700, #ffed4e);
|
||
color: #333;
|
||
padding: 20px 40px;
|
||
border-radius: 15px;
|
||
font-size: 1.5rem;
|
||
font-weight: 700;
|
||
text-align: center;
|
||
box-shadow: 0 10px 30px rgba(255, 215, 0, 0.5);
|
||
z-index: 10000;
|
||
animation: lapCelebrationBounce 1.5s ease-out forwards;
|
||
pointer-events: none;
|
||
`;
|
||
|
||
document.body.appendChild(celebration);
|
||
|
||
// Remove after animation
|
||
setTimeout(() => {
|
||
if (celebration.parentNode) {
|
||
celebration.parentNode.removeChild(celebration);
|
||
}
|
||
}, 1500);
|
||
}
|
||
|
||
// ===== TUNNEL DIGGING SYSTEM FOR SPRINT MODE =====
|
||
|
||
updateTunnelDepth(digger, correctAnswers) {
|
||
// Handle different digger ID formats
|
||
let tunnelId, foxId, depthId;
|
||
if (digger.startsWith('ai-tunnel-')) {
|
||
// AI tunnel format: ai-tunnel-1 -> ai-tunnel-1, ai-fox-1, ai-depth-1
|
||
const aiNum = digger.split('-')[2];
|
||
tunnelId = `ai-tunnel-${aiNum}`;
|
||
foxId = `ai-fox-${aiNum}`;
|
||
depthId = `ai-depth-${aiNum}`;
|
||
} else {
|
||
// Player format: player -> player-tunnel, player-fox, player-depth
|
||
tunnelId = `${digger}-tunnel`;
|
||
foxId = `${digger}-fox`;
|
||
depthId = `${digger}-depth`;
|
||
}
|
||
|
||
const tunnelElement = document.getElementById(tunnelId);
|
||
const foxElement = document.getElementById(foxId);
|
||
const depthElement = document.getElementById(depthId);
|
||
|
||
console.log(`🔍 Updating tunnel for ${digger}: tunnel=${tunnelId}, fox=${foxId}, depth=${depthId}, answers=${correctAnswers}`);
|
||
console.log(`Elements found: tunnel=${!!tunnelElement}, fox=${!!foxElement}, depth=${!!depthElement}`);
|
||
|
||
if (!tunnelElement || !depthElement) return;
|
||
|
||
// Create depth perspective - tunnel shaft stays same size but shows depth visually
|
||
const visualTunnelHeight = 180; // Fixed tunnel shaft height
|
||
const actualDepth = correctAnswers; // Real depth for comparison
|
||
|
||
// Calculate depth levels for visual effects
|
||
const depthLevel = Math.floor(actualDepth / 5); // Every 5 answers = deeper level
|
||
const isDeep = actualDepth > 10;
|
||
const isVeryDeep = actualDepth > 20;
|
||
|
||
// Update tunnel with fixed height but depth effects
|
||
tunnelElement.style.height = visualTunnelHeight + 'px';
|
||
|
||
// Apply depth effects and classes to show how deep we've gone
|
||
tunnelElement.classList.remove('depth-shallow', 'depth-deep', 'depth-very-deep');
|
||
|
||
if (isVeryDeep) {
|
||
// Very deep: dark tunnel with glowing fox
|
||
tunnelElement.classList.add('depth-very-deep');
|
||
tunnelElement.style.background = 'linear-gradient(to bottom, #2c3e50 0%, #1a1a1a 70%, #000000 100%)';
|
||
tunnelElement.style.boxShadow = 'inset 0 0 30px rgba(0,0,0,0.8), 0 0 20px rgba(255,215,0,0.3)';
|
||
if (foxElement) foxElement.style.filter = 'drop-shadow(0 0 8px #ffd700)';
|
||
} else if (isDeep) {
|
||
// Deep: darker tunnel
|
||
tunnelElement.classList.add('depth-deep');
|
||
tunnelElement.style.background = 'linear-gradient(to bottom, #34495e 0%, #2c3e50 100%)';
|
||
tunnelElement.style.boxShadow = 'inset 0 0 25px rgba(0,0,0,0.6)';
|
||
if (foxElement) foxElement.style.filter = 'drop-shadow(0 0 4px #ffa500)';
|
||
} else {
|
||
// Shallow: normal tunnel
|
||
tunnelElement.classList.add('depth-shallow');
|
||
tunnelElement.style.background = '#4a4a4a';
|
||
tunnelElement.style.boxShadow = 'inset 0 0 20px rgba(0,0,0,0.5)';
|
||
if (foxElement) foxElement.style.filter = 'none';
|
||
}
|
||
|
||
// Show depth counter with visual scaling
|
||
depthElement.textContent = correctAnswers;
|
||
if (isVeryDeep) {
|
||
depthElement.style.background = 'rgba(255, 215, 0, 0.95)';
|
||
depthElement.style.color = '#000';
|
||
depthElement.style.transform = 'translateX(-50%) scale(1.1)';
|
||
depthElement.style.boxShadow = '0 0 10px rgba(255, 215, 0, 0.5)';
|
||
} else if (isDeep) {
|
||
depthElement.style.background = 'rgba(255, 152, 0, 0.9)';
|
||
depthElement.style.transform = 'translateX(-50%) scale(1.05)';
|
||
}
|
||
|
||
// Add realistic digging animation to fox
|
||
if (foxElement) {
|
||
// Stop any existing animations
|
||
foxElement.classList.remove('digging', 'idle', 'foxDigDeep');
|
||
|
||
// Add appropriate digging animation based on depth
|
||
if (isVeryDeep) {
|
||
foxElement.classList.add('foxDigDeep');
|
||
setTimeout(() => {
|
||
foxElement.classList.remove('foxDigDeep');
|
||
foxElement.classList.add('idle');
|
||
}, 800);
|
||
} else {
|
||
foxElement.classList.add('digging');
|
||
setTimeout(() => {
|
||
foxElement.classList.remove('digging');
|
||
foxElement.classList.add('idle');
|
||
}, 600);
|
||
}
|
||
|
||
// Create dirt particles effect (more particles for deeper tunnels)
|
||
const particleCount = Math.min(5 + depthLevel, 15);
|
||
this.createDirtParticles(tunnelElement, particleCount);
|
||
|
||
// Add screen shake for very deep digging
|
||
if (isVeryDeep) {
|
||
this.addScreenShake();
|
||
}
|
||
|
||
// Higher treasure chance for deeper tunnels!
|
||
const treasureChance = Math.min(0.15 + (depthLevel * 0.05), 0.4); // Up to 40% for very deep
|
||
if (correctAnswers > 0 && Math.random() < treasureChance) {
|
||
this.foxFindsMemory(digger, correctAnswers, isVeryDeep);
|
||
}
|
||
}
|
||
|
||
console.log(`🦊 ${digger} tunnel depth: ${correctAnswers} answers (Level ${depthLevel}, ${isVeryDeep ? 'VERY DEEP' : isDeep ? 'DEEP' : 'SHALLOW'})`)
|
||
}
|
||
|
||
stopAllSounds() {
|
||
// Aggressive sound stopping - multiple approaches to ensure silence
|
||
try {
|
||
console.log('🔇 Stopping all sounds...');
|
||
|
||
// Method 1: Close all tracked audio contexts
|
||
if (window.audioContexts && window.audioContexts.length > 0) {
|
||
console.log(`🔇 Closing ${window.audioContexts.length} tracked audio contexts`);
|
||
window.audioContexts.forEach((context, index) => {
|
||
try {
|
||
if (context && context.state !== 'closed') {
|
||
console.log(`🔇 Closing audio context ${index + 1}`);
|
||
context.close();
|
||
}
|
||
} catch (closeError) {
|
||
console.log(`🔇 Error closing context ${index + 1}:`, closeError);
|
||
}
|
||
});
|
||
window.audioContexts = [];
|
||
}
|
||
|
||
// Method 2: Try to find and stop any remaining AudioContext instances
|
||
// This is a more aggressive approach for "zombie" sounds
|
||
try {
|
||
// Force garbage collection of audio contexts if possible
|
||
if (window.gc) window.gc();
|
||
} catch (gcError) {
|
||
// GC not available, ignore
|
||
}
|
||
|
||
// Method 3: Clear any remaining audio-related intervals/timeouts
|
||
for (let i = 1; i < 1000; i++) {
|
||
clearTimeout(i);
|
||
clearInterval(i);
|
||
}
|
||
|
||
console.log('🔇 All sounds stopped (aggressive cleanup complete)');
|
||
|
||
} catch (e) {
|
||
console.log('🔇 Sound cleanup error:', e);
|
||
console.log('🔇 Sound cleanup attempted with errors');
|
||
}
|
||
}
|
||
|
||
addScreenShake() {
|
||
// Subtle screen shake for deep digging
|
||
const container = document.querySelector('.race-track-section.tunnel-digging');
|
||
if (container) {
|
||
container.style.animation = 'screenShake 0.4s ease-in-out';
|
||
setTimeout(() => {
|
||
container.style.animation = '';
|
||
}, 400);
|
||
}
|
||
}
|
||
|
||
initializeSteamJourney() {
|
||
// Initialize train journey systems
|
||
this.momentum = 0;
|
||
this.trainPosition = 0; // Position along the route (0-100%)
|
||
this.passengers = [];
|
||
this.lastCorrectAnswerTime = Date.now(); // Track when we last shoveled coal
|
||
this.stations = [
|
||
{ name: 'Depot', position: 0, emoji: '🏭' },
|
||
{ name: 'Hillview', position: 20, emoji: '🏔️' },
|
||
{ name: 'Riverside', position: 40, emoji: '🌊' },
|
||
{ name: 'Summit', position: 60, emoji: '⛰️' },
|
||
{ name: 'Goldfield', position: 80, emoji: '⚱️' },
|
||
{ name: 'Capital', position: 100, emoji: '🏛️' }
|
||
];
|
||
this.gameStartTime = Date.now();
|
||
this.totalScore = 0;
|
||
|
||
// Start the continuous game timer for Lightning Sprint
|
||
this.startGameTimer();
|
||
|
||
// Initialize day/night cycle
|
||
this.updateTimeOfDay();
|
||
|
||
// Start momentum decay (friction) - calibrated for different skill levels
|
||
this.momentumDecayInterval = setInterval(() => {
|
||
if (this.momentum > 0) {
|
||
const decayConfig = this.getMomentumConfig();
|
||
const timeSinceLastCoal = (Date.now() - this.lastCorrectAnswerTime) / 1000; // seconds
|
||
|
||
// Base decay rate increases with higher speeds, but adjusted for skill level
|
||
let decayRate = this.momentum > 75 ? decayConfig.highSpeedDecay :
|
||
this.momentum > 50 ? decayConfig.mediumSpeedDecay :
|
||
decayConfig.baseDecay;
|
||
|
||
// Accelerate decay if no correct answers for a while (but be gentler for kids)
|
||
if (timeSinceLastCoal > decayConfig.starvationThreshold) {
|
||
const extraDecay = Math.min(decayConfig.maxExtraDecay, timeSinceLastCoal / decayConfig.starvationRate);
|
||
decayRate += extraDecay;
|
||
}
|
||
|
||
this.momentum = Math.max(0, this.momentum - decayRate);
|
||
this.updateMomentumDisplay();
|
||
|
||
if (timeSinceLastCoal > decayConfig.warningThreshold && this.momentum > 0) {
|
||
console.log(`🚂 [${this.timeoutSetting}] Train needs coal! ${Math.round(timeSinceLastCoal)}s without fuel, decay rate: ${decayRate.toFixed(1)}%/s`);
|
||
}
|
||
}
|
||
}, 1000);
|
||
|
||
// Start continuous train movement based on momentum
|
||
this.trainMovementInterval = setInterval(() => {
|
||
this.updateTrainPosition();
|
||
}, 200); // Update train position 5 times per second for smooth movement
|
||
|
||
// Generate initial passengers
|
||
this.generatePassengers();
|
||
}
|
||
|
||
updateTimeOfDay() {
|
||
const elapsed = Date.now() - this.gameStartTime;
|
||
const gameProgress = Math.min(elapsed / (60 * 1000), 1); // 60 seconds = full day
|
||
|
||
let timeString, gradient;
|
||
|
||
if (gameProgress < 0.17) {
|
||
timeString = 'Dawn - 5:30 AM';
|
||
gradient = 'var(--dawn-gradient)';
|
||
} else if (gameProgress < 0.33) {
|
||
timeString = 'Morning - 8:00 AM';
|
||
gradient = 'var(--morning-gradient)';
|
||
} else if (gameProgress < 0.67) {
|
||
timeString = 'Midday - 12:00 PM';
|
||
gradient = 'var(--midday-gradient)';
|
||
} else if (gameProgress < 0.83) {
|
||
timeString = 'Afternoon - 4:00 PM';
|
||
gradient = 'var(--afternoon-gradient)';
|
||
} else if (gameProgress < 0.92) {
|
||
timeString = 'Dusk - 7:00 PM';
|
||
gradient = 'var(--dusk-gradient)';
|
||
} else {
|
||
timeString = 'Night - 10:00 PM';
|
||
gradient = 'var(--night-gradient)';
|
||
}
|
||
|
||
const timeDisplay = document.getElementById('time-of-day');
|
||
const steamJourney = document.querySelector('.race-track-section.steam-journey');
|
||
|
||
if (timeDisplay) timeDisplay.textContent = timeString;
|
||
if (steamJourney) steamJourney.style.setProperty('--sky-gradient', gradient);
|
||
}
|
||
|
||
updateMomentumDisplay() {
|
||
const momentumBar = document.getElementById('momentum-bar');
|
||
const momentumLabel = document.getElementById('momentum-label');
|
||
|
||
if (momentumBar) {
|
||
momentumBar.style.width = this.momentum + '%';
|
||
}
|
||
|
||
if (momentumLabel) {
|
||
let status = 'Crawling';
|
||
if (this.momentum > 70) status = 'Full Steam!';
|
||
else if (this.momentum > 40) status = 'Good Speed';
|
||
else if (this.momentum > 15) status = 'Building Steam';
|
||
|
||
momentumLabel.textContent = `Momentum: ${Math.round(this.momentum)}% - ${status}`;
|
||
}
|
||
|
||
// Update pressure gauge
|
||
this.updatePressureGauge();
|
||
}
|
||
|
||
updatePressureGauge() {
|
||
const pressureArc = document.getElementById('pressure-arc');
|
||
const pressureNeedle = document.getElementById('pressure-needle');
|
||
const pressureValue = document.getElementById('pressure-value');
|
||
|
||
if (pressureArc && pressureNeedle && pressureValue) {
|
||
// Convert momentum (0-100) to gauge values
|
||
const pressure = Math.round(this.momentum);
|
||
const psi = Math.round(pressure * 1.5); // Scale to 0-150 PSI range
|
||
|
||
// Update arc progress (circumference is ~251.2 pixels)
|
||
const circumference = 251.2;
|
||
const offset = circumference - (pressure / 100) * circumference;
|
||
pressureArc.style.strokeDashoffset = offset;
|
||
|
||
// Update needle rotation (-90 to +90 degrees for half-circle)
|
||
const rotation = -90 + (pressure / 100) * 180;
|
||
pressureNeedle.style.transform = `rotate(${rotation}deg)`;
|
||
|
||
// Update pressure value display
|
||
pressureValue.textContent = `${psi} PSI`;
|
||
|
||
// Change gauge color based on pressure (playful colors)
|
||
let gaugeColor = '#ff6b6b'; // Coral red for low pressure
|
||
if (pressure > 70) gaugeColor = '#4ecdc4'; // Turquoise for high pressure
|
||
else if (pressure > 40) gaugeColor = '#feca57'; // Sunny yellow for medium pressure
|
||
|
||
pressureArc.style.stroke = gaugeColor;
|
||
}
|
||
}
|
||
|
||
generatePassengers() {
|
||
// Generate 3-5 random passengers with destinations
|
||
const passengerCount = 3 + Math.floor(Math.random() * 3);
|
||
this.passengers = [];
|
||
|
||
const names = ['Alice', 'Bob', 'Carol', 'Dave', 'Eve', 'Frank', 'Grace', 'Henry'];
|
||
const usedNames = new Set();
|
||
|
||
for (let i = 0; i < passengerCount; i++) {
|
||
let name;
|
||
do {
|
||
name = names[Math.floor(Math.random() * names.length)];
|
||
} while (usedNames.has(name));
|
||
usedNames.add(name);
|
||
|
||
// Pick a random destination station (not depot)
|
||
const destinationStations = this.stations.slice(1); // Skip depot
|
||
const destination = destinationStations[Math.floor(Math.random() * destinationStations.length)];
|
||
|
||
this.passengers.push({
|
||
name: name,
|
||
destination: destination.name,
|
||
emoji: destination.emoji,
|
||
urgent: Math.random() < 0.3 // 30% chance of urgent passenger
|
||
});
|
||
}
|
||
|
||
this.updatePassengerDisplay();
|
||
}
|
||
|
||
updatePassengerDisplay() {
|
||
const passengerList = document.getElementById('passenger-list');
|
||
if (!passengerList) return;
|
||
|
||
passengerList.innerHTML = '';
|
||
this.passengers.forEach(passenger => {
|
||
const passengerDiv = document.createElement('div');
|
||
passengerDiv.className = 'passenger' + (passenger.urgent ? ' urgent' : '');
|
||
passengerDiv.textContent = `${passenger.name} → ${passenger.emoji}`;
|
||
passengerList.appendChild(passengerDiv);
|
||
});
|
||
}
|
||
|
||
shovelsCoal() {
|
||
// Called when player gets correct answer - adds momentum
|
||
const coalWorker = document.getElementById('coal-worker');
|
||
if (coalWorker) {
|
||
coalWorker.classList.remove('shoveling');
|
||
coalWorker.classList.add('shoveling');
|
||
setTimeout(() => coalWorker.classList.remove('shoveling'), 800);
|
||
}
|
||
|
||
// Reset the coal timer - fresh fuel for the train!
|
||
this.lastCorrectAnswerTime = Date.now();
|
||
|
||
// Create coal particle effects
|
||
this.createCoalParticles();
|
||
|
||
// Create steam puffs from train
|
||
this.createSteamPuffs();
|
||
|
||
// Increase momentum (coal gives energy) - adaptive based on difficulty
|
||
const config = this.getMomentumConfig();
|
||
this.momentum = Math.min(100, this.momentum + config.momentumGain);
|
||
this.updateMomentumDisplay();
|
||
|
||
// Flash momentum bar to show boost
|
||
this.flashMomentumBar();
|
||
|
||
// Update time of day
|
||
this.updateTimeOfDay();
|
||
|
||
// Play train whistle for celebrations and milestones
|
||
if (this.streak >= 5 && this.streak % 3 === 0) {
|
||
// Major milestone - play train whistle
|
||
setTimeout(() => {
|
||
this.playSound('train_whistle', 0.4);
|
||
}, 200);
|
||
console.log(`🚂💨 TOOT TOOT! Milestone whistle for streak of ${this.streak}!`);
|
||
} else if (this.momentum >= 90) {
|
||
// High momentum celebration - occasional whistle
|
||
if (Math.random() < 0.3) {
|
||
setTimeout(() => {
|
||
this.playSound('train_whistle', 0.25);
|
||
}, 150);
|
||
}
|
||
}
|
||
|
||
this.playSound('correct');
|
||
}
|
||
|
||
createCoalParticles() {
|
||
// Create flying coal particles from shoveler to train
|
||
const routeMap = document.querySelector('.route-map');
|
||
if (!routeMap) return;
|
||
|
||
for (let i = 0; i < 5; i++) {
|
||
const particle = document.createElement('div');
|
||
particle.className = 'coal-particle';
|
||
|
||
// Random size between 4-8px
|
||
const size = 4 + Math.random() * 4;
|
||
particle.style.width = size + 'px';
|
||
particle.style.height = size + 'px';
|
||
|
||
// Start position near coal worker
|
||
particle.style.left = (60 + Math.random() * 20) + 'px';
|
||
particle.style.top = (350 + Math.random() * 20) + 'px';
|
||
|
||
// Apply animation with delay
|
||
particle.style.animation = `coalFly 0.8s ease-out ${i * 0.1}s forwards`;
|
||
|
||
routeMap.appendChild(particle);
|
||
|
||
// Remove particle after animation
|
||
setTimeout(() => {
|
||
if (particle.parentNode) {
|
||
particle.parentNode.removeChild(particle);
|
||
}
|
||
}, 800 + (i * 100));
|
||
}
|
||
}
|
||
|
||
createSteamPuffs() {
|
||
// Create steam puffs from the locomotive
|
||
const routeMap = document.querySelector('.route-map');
|
||
const locomotive = document.getElementById('train-position');
|
||
if (!routeMap || !locomotive) return;
|
||
|
||
const trainRect = locomotive.getBoundingClientRect();
|
||
const mapRect = routeMap.getBoundingClientRect();
|
||
|
||
for (let i = 0; i < 3; i++) {
|
||
const puff = document.createElement('div');
|
||
puff.className = 'steam-puff';
|
||
|
||
// Random size between 15-25px
|
||
const size = 15 + Math.random() * 10;
|
||
puff.style.width = size + 'px';
|
||
puff.style.height = size + 'px';
|
||
|
||
// Position near locomotive chimney
|
||
const relativeLeft = trainRect.left - mapRect.left - 10 + Math.random() * 20;
|
||
const relativeTop = trainRect.top - mapRect.top - 15 + Math.random() * 10;
|
||
|
||
puff.style.left = relativeLeft + 'px';
|
||
puff.style.top = relativeTop + 'px';
|
||
|
||
// Apply animation with delay
|
||
puff.style.animation = `steamRise 1.2s ease-out ${i * 0.2}s forwards`;
|
||
|
||
routeMap.appendChild(puff);
|
||
|
||
// Remove puff after animation
|
||
setTimeout(() => {
|
||
if (puff.parentNode) {
|
||
puff.parentNode.removeChild(puff);
|
||
}
|
||
}, 1200 + (i * 200));
|
||
}
|
||
}
|
||
|
||
flashMomentumBar() {
|
||
// Flash the momentum bar to show boost
|
||
const momentumBar = document.getElementById('momentum-bar');
|
||
if (momentumBar) {
|
||
momentumBar.classList.remove('momentum-boost');
|
||
momentumBar.classList.add('momentum-boost');
|
||
setTimeout(() => momentumBar.classList.remove('momentum-boost'), 800);
|
||
}
|
||
}
|
||
|
||
getMomentumConfig() {
|
||
// Return momentum settings based on difficulty level (timeout setting)
|
||
switch (this.timeoutSetting) {
|
||
case 'preschool': // 4-year-olds like Fern - forgiving but still challenging
|
||
return {
|
||
momentumGain: 15, // More momentum per correct answer
|
||
baseDecay: 1.5, // Noticeable decay to keep it interesting
|
||
mediumSpeedDecay: 2.0, // Momentum matters at medium speed
|
||
highSpeedDecay: 2.5, // High speed requires attention
|
||
wrongAnswerPenalty: 3, // Small penalty for mistakes
|
||
starvationThreshold: 25, // 25 seconds before starvation kicks in
|
||
starvationRate: 15, // Very slow starvation rate
|
||
maxExtraDecay: 1.5, // Gentle extra decay
|
||
warningThreshold: 30
|
||
};
|
||
|
||
case 'kindergarten': // 5-6 year olds - gentle but engaging
|
||
return {
|
||
momentumGain: 12,
|
||
baseDecay: 1.8, // Increased from 0.8
|
||
mediumSpeedDecay: 2.2, // Increased from 1.2
|
||
highSpeedDecay: 2.8, // Increased from 1.8
|
||
wrongAnswerPenalty: 4,
|
||
starvationThreshold: 20,
|
||
starvationRate: 12,
|
||
maxExtraDecay: 2, // Increased from 1.5
|
||
warningThreshold: 25
|
||
};
|
||
|
||
case 'relaxed': // Casual players - should still feel momentum pressure
|
||
return {
|
||
momentumGain: 10,
|
||
baseDecay: 2.0, // Increased from 1.2
|
||
mediumSpeedDecay: 2.5, // Increased from 1.8
|
||
highSpeedDecay: 3.0, // Increased from 2.2
|
||
wrongAnswerPenalty: 5,
|
||
starvationThreshold: 15,
|
||
starvationRate: 10,
|
||
maxExtraDecay: 2.5, // Increased from 2
|
||
warningThreshold: 20
|
||
};
|
||
|
||
case 'slow': // Learning mode
|
||
return {
|
||
momentumGain: 8,
|
||
baseDecay: 1.5,
|
||
mediumSpeedDecay: 2.0,
|
||
highSpeedDecay: 2.5,
|
||
wrongAnswerPenalty: 6,
|
||
starvationThreshold: 12,
|
||
starvationRate: 8,
|
||
maxExtraDecay: 2.5,
|
||
warningThreshold: 18
|
||
};
|
||
|
||
case 'normal': // Default balanced
|
||
return {
|
||
momentumGain: 8,
|
||
baseDecay: 2,
|
||
mediumSpeedDecay: 2.5,
|
||
highSpeedDecay: 3,
|
||
wrongAnswerPenalty: 8,
|
||
starvationThreshold: 10,
|
||
starvationRate: 5,
|
||
maxExtraDecay: 3,
|
||
warningThreshold: 15
|
||
};
|
||
|
||
case 'fast': // Challenge mode
|
||
return {
|
||
momentumGain: 6,
|
||
baseDecay: 2.5,
|
||
mediumSpeedDecay: 3.2,
|
||
highSpeedDecay: 4,
|
||
wrongAnswerPenalty: 10,
|
||
starvationThreshold: 8,
|
||
starvationRate: 4,
|
||
maxExtraDecay: 4,
|
||
warningThreshold: 12
|
||
};
|
||
|
||
case 'expert': // Expert mode - original aggressive settings
|
||
return {
|
||
momentumGain: 5,
|
||
baseDecay: 3,
|
||
mediumSpeedDecay: 4,
|
||
highSpeedDecay: 5,
|
||
wrongAnswerPenalty: 12,
|
||
starvationThreshold: 6,
|
||
starvationRate: 3,
|
||
maxExtraDecay: 5,
|
||
warningThreshold: 10
|
||
};
|
||
|
||
default:
|
||
return this.getMomentumConfig.call({timeoutSetting: 'normal'});
|
||
}
|
||
}
|
||
|
||
loseMomentum() {
|
||
// Called when player gets wrong answer or times out - DRAMATIC CONSEQUENCES!
|
||
// Wrong answers cause complete momentum loss and coal spilling
|
||
const previousMomentum = this.momentum;
|
||
this.momentum = 0; // Complete momentum loss for wrong answers!
|
||
|
||
// Trigger dramatic coal spilling animation
|
||
this.triggerCoalSpilling();
|
||
|
||
// Update displays
|
||
this.updateMomentumDisplay();
|
||
this.updatePressureGauge();
|
||
|
||
console.log(`💥 COAL SPILLING! Train momentum dropped from ${previousMomentum}% to 0% (DRAMATIC CONSEQUENCES)`);
|
||
|
||
// Play coal spilling sound with multiple chunks
|
||
this.playCoalSpillingSounds();
|
||
}
|
||
|
||
triggerCoalSpilling() {
|
||
// Create dramatic coal spilling visual effects
|
||
const routeContainer = document.querySelector('.route-path');
|
||
if (!routeContainer) return;
|
||
|
||
// Get train position for coal spilling location
|
||
const locomotive = document.getElementById('train-position');
|
||
if (!locomotive) return;
|
||
|
||
const trainRect = locomotive.getBoundingClientRect();
|
||
const containerRect = routeContainer.getBoundingClientRect();
|
||
|
||
// Create multiple coal chunks that spill from the train
|
||
for (let i = 0; i < 8; i++) {
|
||
const coalChunk = document.createElement('div');
|
||
coalChunk.className = 'coal-chunk';
|
||
coalChunk.textContent = '⬛';
|
||
coalChunk.style.position = 'absolute';
|
||
coalChunk.style.fontSize = '1.2rem';
|
||
coalChunk.style.zIndex = '50';
|
||
coalChunk.style.pointerEvents = 'none';
|
||
|
||
// Start position near the train
|
||
const startX = (trainRect.left - containerRect.left) + (Math.random() - 0.5) * 30;
|
||
const startY = (trainRect.top - containerRect.top) + (Math.random() - 0.5) * 20;
|
||
coalChunk.style.left = startX + 'px';
|
||
coalChunk.style.top = startY + 'px';
|
||
|
||
// Add dramatic spilling animation
|
||
coalChunk.style.animation = `coalSpill ${0.8 + Math.random() * 0.4}s ease-out forwards`;
|
||
coalChunk.style.animationDelay = `${i * 0.1}s`;
|
||
|
||
routeContainer.appendChild(coalChunk);
|
||
|
||
// Remove chunk after animation
|
||
setTimeout(() => {
|
||
if (coalChunk.parentNode) {
|
||
coalChunk.parentNode.removeChild(coalChunk);
|
||
}
|
||
}, 1500 + (i * 100));
|
||
}
|
||
|
||
// Flash pressure gauge red to show dramatic failure
|
||
const pressureGauge = document.querySelector('.pressure-gauge-container');
|
||
if (pressureGauge) {
|
||
pressureGauge.style.animation = 'pressureAlarm 0.8s ease-in-out';
|
||
setTimeout(() => {
|
||
pressureGauge.style.animation = '';
|
||
}, 800);
|
||
}
|
||
}
|
||
|
||
playCoalSpillingSounds() {
|
||
// Play realistic coal chunk falling sounds
|
||
for (let i = 0; i < 4; i++) {
|
||
setTimeout(() => {
|
||
this.playSound('coal_spill', 0.3);
|
||
}, i * 150); // Staggered coal chunks falling
|
||
}
|
||
|
||
// Add a final "clatter" with incorrect sound
|
||
setTimeout(() => {
|
||
this.playSound('incorrect', 0.2);
|
||
}, 600);
|
||
|
||
console.log('🔊 Playing realistic coal spilling sound effects');
|
||
}
|
||
|
||
updateTrainPosition() {
|
||
// Move train along the route based on momentum
|
||
if (this.momentum > 0) {
|
||
const speed = this.momentum / 100; // Convert to 0-1 speed multiplier
|
||
this.trainPosition += speed * 0.4; // Continuous movement rate (called 5x per second)
|
||
|
||
// Check for route completion before capping at 100
|
||
if (this.trainPosition >= 100) {
|
||
this.handleRouteCompletion();
|
||
return; // Route completion will reset position
|
||
}
|
||
|
||
// Update visual position along the path
|
||
this.updateTrainVisualization();
|
||
|
||
// Play momentum-based train sounds (chuffing varies with speed)
|
||
this.updateMomentumBasedAudio(speed);
|
||
|
||
// Check for station arrivals and passenger deliveries
|
||
this.checkStationArrivals();
|
||
}
|
||
}
|
||
|
||
handleRouteCompletion() {
|
||
// Prevent multiple calls during transition
|
||
if (this.isRouteSwitching) return;
|
||
this.isRouteSwitching = true;
|
||
|
||
console.log(`🎉 ROUTE COMPLETED! Moving to new world (Route ${this.currentRoute + 1})`);
|
||
|
||
// CRITICAL: Clean up all timers to prevent chaos
|
||
this.cleanupRouteTimers();
|
||
|
||
// Play celebration whistle
|
||
this.playSound('train_whistle', 0.6);
|
||
setTimeout(() => {
|
||
this.playSound('celebration', 0.4);
|
||
}, 800);
|
||
|
||
// Initialize route progression if not set
|
||
if (!this.currentRoute) {
|
||
this.currentRoute = 1;
|
||
this.totalDistance = 0;
|
||
}
|
||
|
||
// Track progress
|
||
this.currentRoute++;
|
||
this.totalDistance += 100; // Each route is 100 units
|
||
|
||
// Show route completion celebration
|
||
this.showRouteCompletionCelebration();
|
||
|
||
// Generate new route after brief celebration
|
||
setTimeout(() => {
|
||
this.generateNewRoute();
|
||
}, 3000);
|
||
}
|
||
|
||
cleanupRouteTimers() {
|
||
// Stop all running intervals to prevent chaos during route transition
|
||
console.log('🧹 Cleaning up route timers...');
|
||
|
||
if (this.momentumDecayInterval) {
|
||
clearInterval(this.momentumDecayInterval);
|
||
this.momentumDecayInterval = null;
|
||
}
|
||
|
||
if (this.trainMovementInterval) {
|
||
clearInterval(this.trainMovementInterval);
|
||
this.trainMovementInterval = null;
|
||
}
|
||
|
||
if (this.displaySwitchTimeout) {
|
||
clearTimeout(this.displaySwitchTimeout);
|
||
this.displaySwitchTimeout = null;
|
||
}
|
||
|
||
// Clear any auto-submit timeouts
|
||
this.clearAutoSubmitTimeout();
|
||
|
||
// Clear celebration timeouts to prevent overlapping
|
||
if (this.celebrationTimeout) {
|
||
clearTimeout(this.celebrationTimeout);
|
||
this.celebrationTimeout = null;
|
||
}
|
||
}
|
||
|
||
showRouteCompletionCelebration() {
|
||
// Create celebration overlay
|
||
const routeContainer = document.querySelector('.route-path');
|
||
if (!routeContainer) return;
|
||
|
||
const celebration = document.createElement('div');
|
||
celebration.className = 'route-completion-celebration';
|
||
celebration.innerHTML = `
|
||
<div class="celebration-content">
|
||
<h2>🎉 ROUTE COMPLETED! 🎉</h2>
|
||
<p>Excellent work! Moving to Route ${this.currentRoute}</p>
|
||
<div class="progress-stats">
|
||
<div>📍 Total Distance: ${this.totalDistance} km</div>
|
||
<div>🚂 Routes Completed: ${this.currentRoute - 1}</div>
|
||
<div>✅ Correct Answers: ${this.correctAnswers}</div>
|
||
</div>
|
||
</div>
|
||
`;
|
||
|
||
routeContainer.appendChild(celebration);
|
||
|
||
// Remove celebration after animation
|
||
setTimeout(() => {
|
||
if (celebration.parentNode) {
|
||
celebration.parentNode.removeChild(celebration);
|
||
}
|
||
}, 2800);
|
||
}
|
||
|
||
generateNewRoute() {
|
||
// Reset train position to start of new route
|
||
this.trainPosition = 0;
|
||
|
||
// Generate new track layout with different theme
|
||
this.generateDynamicTrack();
|
||
|
||
// Update route theme/scenery
|
||
this.updateRouteTheme();
|
||
|
||
// Update route progress display
|
||
this.updateRouteProgressDisplay();
|
||
|
||
// Generate new passengers for this route
|
||
this.generatePassengers();
|
||
|
||
// Restart essential timers for new route
|
||
this.restartRouteTimers();
|
||
|
||
console.log(`🌟 NEW ROUTE GENERATED! Welcome to Route ${this.currentRoute} - ${this.getRouteThemeName()}`);
|
||
|
||
// Play "all aboard" whistle
|
||
setTimeout(() => {
|
||
this.playSound('train_whistle', 0.4);
|
||
}, 500);
|
||
|
||
// Clear route switching flag
|
||
this.isRouteSwitching = false;
|
||
}
|
||
|
||
restartRouteTimers() {
|
||
// Restart momentum decay interval
|
||
this.momentumDecayInterval = setInterval(() => {
|
||
if (this.momentum > 0) {
|
||
const decayConfig = this.getMomentumConfig();
|
||
const timeSinceLastCoal = (Date.now() - this.lastCorrectAnswerTime) / 1000;
|
||
|
||
// Base decay rate increases with higher speeds, but adjusted for skill level
|
||
let decayRate = this.momentum > 75 ? decayConfig.highSpeedDecay :
|
||
this.momentum > 50 ? decayConfig.mediumSpeedDecay :
|
||
decayConfig.lowSpeedDecay;
|
||
|
||
// Exponential penalty after coal starvation threshold
|
||
if (timeSinceLastCoal > decayConfig.starvationThreshold) {
|
||
const starvationMultiplier = Math.pow(1.5, Math.floor(timeSinceLastCoal / decayConfig.starvationThreshold));
|
||
decayRate *= Math.min(starvationMultiplier, 10); // Cap at 10x
|
||
}
|
||
|
||
this.momentum = Math.max(0, this.momentum - decayRate);
|
||
this.updateMomentumDisplay();
|
||
}
|
||
}, 1000);
|
||
|
||
// Restart train movement interval
|
||
this.trainMovementInterval = setInterval(() => {
|
||
this.updateTrainPosition();
|
||
}, 200); // Update train position 5 times per second for smooth movement
|
||
|
||
console.log('🔄 Route timers restarted successfully');
|
||
}
|
||
|
||
updateRouteProgressDisplay() {
|
||
const routeProgressEl = document.getElementById('route-progress');
|
||
if (routeProgressEl) {
|
||
const routeName = this.getRouteThemeName();
|
||
routeProgressEl.textContent = `Route ${this.currentRoute} - ${routeName}`;
|
||
}
|
||
}
|
||
|
||
getRouteThemeName() {
|
||
const themes = [
|
||
"Mountain Valley Express",
|
||
"Coastal Railway Adventure",
|
||
"Forest Journey Trail",
|
||
"Desert Oasis Line",
|
||
"Prairie Meadow Route",
|
||
"Riverside Scenic Railway",
|
||
"Highland Express",
|
||
"Lakeside Loop",
|
||
"Countryside Connection",
|
||
"Sunset Mesa Line"
|
||
];
|
||
return themes[(this.currentRoute - 1) % themes.length] || "Mystery Route";
|
||
}
|
||
|
||
updateRouteTheme() {
|
||
// Cycle through different time-of-day themes for variety
|
||
const themes = ['dawn', 'morning', 'midday', 'afternoon', 'dusk'];
|
||
const themeIndex = (this.currentRoute - 1) % themes.length;
|
||
const themeName = themes[themeIndex];
|
||
|
||
const steamJourney = document.querySelector('.race-track-section.steam-journey');
|
||
if (steamJourney) {
|
||
steamJourney.style.setProperty('--sky-gradient', `var(--${themeName}-gradient)`);
|
||
console.log(`🎨 Route theme set to: ${themeName}`);
|
||
}
|
||
}
|
||
|
||
updateTrainVisualization() {
|
||
const locomotive = document.getElementById('train-position');
|
||
if (!locomotive) return;
|
||
|
||
// Get the actual SVG path and use it for precise positioning
|
||
const routePath = document.querySelector('.train-route');
|
||
if (!routePath) return;
|
||
|
||
const progress = this.trainPosition / 100;
|
||
const pathLength = routePath.getTotalLength();
|
||
const targetLength = progress * pathLength;
|
||
|
||
// Get the exact point on the curved path
|
||
const point = routePath.getPointAtLength(targetLength);
|
||
|
||
// Calculate train rotation based on path direction
|
||
const lookAheadDistance = Math.min(5, pathLength - targetLength); // Look 5 units ahead (or to end)
|
||
const nextPoint = routePath.getPointAtLength(targetLength + lookAheadDistance);
|
||
|
||
// Calculate angle between current point and next point
|
||
const deltaX = nextPoint.x - point.x;
|
||
const deltaY = nextPoint.y - point.y;
|
||
const angleRadians = Math.atan2(deltaY, deltaX);
|
||
const angleDegrees = angleRadians * (180 / Math.PI);
|
||
|
||
// Apply position and rotation to locomotive
|
||
locomotive.style.left = point.x + 'px';
|
||
locomotive.style.top = point.y + 'px';
|
||
locomotive.style.transform = `translate(-50%, -50%) rotate(${angleDegrees}deg)`;
|
||
locomotive.style.transformOrigin = 'center center';
|
||
|
||
// Update floating math display position to follow the train
|
||
// Position train math display above the locomotive if following mode is active
|
||
if (this.isDisplayMoving) {
|
||
let trainMath = document.getElementById('train-math-display');
|
||
if (!trainMath) {
|
||
// Create train math display element
|
||
trainMath = document.createElement('div');
|
||
trainMath.id = 'train-math-display';
|
||
trainMath.className = 'train-math-display';
|
||
|
||
// Add to same container as the train
|
||
const journeyContainer = document.querySelector('.journey-container');
|
||
if (journeyContainer) {
|
||
journeyContainer.appendChild(trainMath);
|
||
console.log('🎈 Created train math display attached to train');
|
||
}
|
||
}
|
||
|
||
if (trainMath) {
|
||
// Update content and position
|
||
const challengeNum = document.getElementById('challenge-number').textContent;
|
||
const targetNum = document.getElementById('target-number').textContent;
|
||
trainMath.textContent = `${challengeNum} + ? = ${targetNum}`;
|
||
trainMath.style.left = point.x + 'px';
|
||
trainMath.style.top = (point.y - 60) + 'px';
|
||
}
|
||
} else {
|
||
// Remove train math display when not following train
|
||
const trainMath = document.getElementById('train-math-display');
|
||
if (trainMath) {
|
||
trainMath.remove();
|
||
}
|
||
}
|
||
|
||
console.log(`🚂 Train at position ${Math.round(this.trainPosition)}% - ${Math.round(point.x)}, ${Math.round(point.y)} (on track)`);
|
||
}
|
||
|
||
updateMomentumBasedAudio(speed) {
|
||
// Play chuffing sounds that vary with train speed/momentum
|
||
const now = Date.now();
|
||
|
||
// Initialize chuff timing if not set
|
||
if (!this.lastChuffTime) {
|
||
this.lastChuffTime = now;
|
||
this.chuffInterval = 1000; // Start with slow chuffing
|
||
}
|
||
|
||
// Calculate chuffing interval based on speed (faster train = faster chuffs)
|
||
// Speed range: 0-1, chuff interval: 2000ms (slow) to 300ms (fast)
|
||
this.chuffInterval = Math.max(300, 2000 - (speed * 1700));
|
||
|
||
// Play chuff sound if enough time has passed
|
||
if (now - this.lastChuffTime >= this.chuffInterval) {
|
||
// Higher speed = louder chuffs (more steam)
|
||
const chuffVolume = Math.min(0.4, 0.1 + (speed * 0.3));
|
||
this.playSound('train_chuff', chuffVolume);
|
||
|
||
// Occasionally add steam hiss at high speeds
|
||
if (speed > 0.7 && Math.random() < 0.3) {
|
||
setTimeout(() => {
|
||
this.playSound('steam_hiss', chuffVolume * 0.5);
|
||
}, 100);
|
||
}
|
||
|
||
this.lastChuffTime = now;
|
||
console.log(`🚂 Chuff! Speed: ${Math.round(speed * 100)}%, Interval: ${Math.round(this.chuffInterval)}ms`);
|
||
}
|
||
}
|
||
|
||
handleDisplaySwitching() {
|
||
// Only apply display switching logic in Lightning Sprint train mode
|
||
if (!document.querySelector('.route-path')) return;
|
||
|
||
// After 2 seconds, enable train-following mode for math display
|
||
if (this.displaySwitchTimeout) {
|
||
clearTimeout(this.displaySwitchTimeout);
|
||
}
|
||
|
||
this.displaySwitchTimeout = setTimeout(() => {
|
||
this.isDisplayMoving = true;
|
||
console.log('🎈 Display switching activated - math will follow train in 2 seconds');
|
||
}, 2000);
|
||
}
|
||
|
||
checkStationArrivals() {
|
||
// Check if we've reached any stations where passengers want to get off
|
||
this.stations.forEach(station => {
|
||
const stationReached = this.trainPosition >= station.position;
|
||
if (stationReached) {
|
||
// Deliver passengers to this station
|
||
const deliveredPassengers = this.passengers.filter(p => p.destination === station.name);
|
||
if (deliveredPassengers.length > 0) {
|
||
deliveredPassengers.forEach(passenger => {
|
||
const points = passenger.urgent ? 20 : 10;
|
||
this.totalScore += points;
|
||
console.log(`🎯 Delivered ${passenger.name} to ${station.name} for ${points} points!`);
|
||
});
|
||
|
||
// Remove delivered passengers
|
||
this.passengers = this.passengers.filter(p => p.destination !== station.name);
|
||
this.updatePassengerDisplay();
|
||
|
||
this.playSound('milestone');
|
||
}
|
||
}
|
||
});
|
||
}
|
||
|
||
checkTrainJourneyCompletion() {
|
||
// Don't check completion in the first 10 seconds to allow game to get started
|
||
const elapsed = (Date.now() - this.gameStartTime) / 1000;
|
||
if (elapsed < 10) return;
|
||
|
||
// For Lightning Sprint train mode, ONLY end when train reaches the end of the route
|
||
const journeyComplete = this.trainPosition >= 100; // Reached the final station (Capital)
|
||
|
||
// Only consider ending early if train is truly stuck (very strict conditions)
|
||
const config = this.getMomentumConfig();
|
||
const timeSinceLastCoal = (Date.now() - this.lastCorrectAnswerTime) / 1000;
|
||
const trainStuck = this.momentum <= 0 &&
|
||
this.trainPosition < 5 &&
|
||
this.correctAnswers > 5 &&
|
||
timeSinceLastCoal > (config.starvationThreshold * 2); // Much longer grace period
|
||
|
||
if (journeyComplete) {
|
||
// Check if we're in endless steam journey mode or regular race mode
|
||
const isEndlessSteamJourney = document.querySelector('.route-path'); // Steam journey has route-path
|
||
|
||
if (isEndlessSteamJourney) {
|
||
// Don't end race - let the endless route progression handle this
|
||
console.log('🚂 Journey complete detected but endless mode active - route progression will handle');
|
||
return;
|
||
} else {
|
||
// Regular race mode - end normally
|
||
const score = this.totalScore;
|
||
const deliveredCount = this.correctAnswers;
|
||
this.endRace(`🎉 Journey Complete! Reached Capital Station! Score: ${score} points from ${deliveredCount} deliveries!`);
|
||
}
|
||
} else if (trainStuck) {
|
||
// Train is truly stuck - but this should be very rare now with adaptive difficulty
|
||
const isEndlessSteamJourney = document.querySelector('.route-path');
|
||
|
||
if (isEndlessSteamJourney) {
|
||
console.log('🚂 Train stuck detected but endless mode active - generating momentum boost');
|
||
// In endless mode, give a momentum boost instead of ending
|
||
this.momentum = Math.max(10, this.momentum + 5); // Small boost to keep going
|
||
this.updateMomentumDisplay();
|
||
return;
|
||
} else {
|
||
// Regular race mode - end normally
|
||
const progress = Math.round(this.trainPosition);
|
||
console.log(`🚂 Train considered stuck: momentum=${this.momentum}, position=${this.trainPosition}%, answers=${this.correctAnswers}, time since coal=${Math.round(timeSinceLastCoal)}s`);
|
||
this.endRace(`🚂 Train ran out of steam at ${progress}% of the journey! Try an easier difficulty mode for more forgiving momentum!`);
|
||
}
|
||
}
|
||
|
||
// If all passengers delivered but train hasn't finished route, generate more passengers!
|
||
if (this.passengers.length === 0 && this.trainPosition < 90) {
|
||
console.log(`🚂 Generating more passengers - journey continues! Position: ${Math.round(this.trainPosition)}%`);
|
||
this.generatePassengers();
|
||
}
|
||
}
|
||
|
||
createDirtParticles(tunnelElement, particleCount = 5) {
|
||
// Create flying dirt particles when digging (more for deeper tunnels)
|
||
for (let i = 0; i < particleCount; i++) {
|
||
const particle = document.createElement('div');
|
||
particle.className = 'dirt-particle';
|
||
|
||
// Random trajectory
|
||
const flyX = (Math.random() - 0.5) * 100;
|
||
const flyY = -30 - Math.random() * 40;
|
||
particle.style.setProperty('--fly-x', flyX + 'px');
|
||
particle.style.setProperty('--fly-y', flyY + 'px');
|
||
|
||
// Position at tunnel opening
|
||
const rect = tunnelElement.getBoundingClientRect();
|
||
particle.style.left = (rect.left + rect.width / 2) + 'px';
|
||
particle.style.top = rect.top + 'px';
|
||
particle.style.position = 'fixed';
|
||
particle.style.zIndex = '100';
|
||
|
||
document.body.appendChild(particle);
|
||
|
||
// Remove after animation
|
||
setTimeout(() => {
|
||
if (particle.parentNode) {
|
||
particle.parentNode.removeChild(particle);
|
||
}
|
||
}, 600);
|
||
}
|
||
}
|
||
|
||
foxFindsMemory(digger, correctAnswers, isVeryDeep = false) {
|
||
// Different treasures for different depths
|
||
const shallowTreasures = ['🟡', '🔶', '🟠', '🔸', '✨'];
|
||
const deepTreasures = ['💎', '⭐', '💰', '🏆', '🎯'];
|
||
const veryDeepTreasures = ['💎', '👑', '🏆', '⚡', '🌟', '🔥'];
|
||
|
||
const shallowMemories = [
|
||
'Found a shiny pebble!',
|
||
'Discovered a small gem!',
|
||
'Unearthed a crystal!',
|
||
'Found a gold flake!'
|
||
];
|
||
|
||
const deepMemories = [
|
||
'Discovered an ancient coin!',
|
||
'Found a golden nugget!',
|
||
'Discovered buried treasure!',
|
||
'Unearthed a precious stone!',
|
||
'Discovered a lucky charm!'
|
||
];
|
||
|
||
const veryDeepMemories = [
|
||
'FOUND A LEGENDARY DIAMOND!',
|
||
'DISCOVERED ANCIENT TREASURE!',
|
||
'UNEARTHED A ROYAL CROWN!',
|
||
'FOUND THE ULTIMATE GEM!',
|
||
'DISCOVERED MAGICAL ARTIFACT!'
|
||
];
|
||
|
||
// Select appropriate treasure and memory based on depth
|
||
let treasures, memories;
|
||
if (isVeryDeep) {
|
||
treasures = veryDeepTreasures;
|
||
memories = veryDeepMemories;
|
||
} else if (correctAnswers > 10) {
|
||
treasures = deepTreasures;
|
||
memories = deepMemories;
|
||
} else {
|
||
treasures = shallowTreasures;
|
||
memories = shallowMemories;
|
||
}
|
||
|
||
const treasure = treasures[Math.floor(Math.random() * treasures.length)];
|
||
const memory = memories[Math.floor(Math.random() * memories.length)];
|
||
|
||
// Create floating treasure with depth-based effects
|
||
const treasureElement = document.createElement('div');
|
||
treasureElement.textContent = treasure;
|
||
const treasureSize = isVeryDeep ? '2rem' : correctAnswers > 10 ? '1.7rem' : '1.5rem';
|
||
const glowEffect = isVeryDeep ? 'filter: drop-shadow(0 0 10px #ffd700);' : '';
|
||
|
||
treasureElement.style.cssText = `
|
||
position: fixed;
|
||
font-size: ${treasureSize};
|
||
z-index: 10000;
|
||
pointer-events: none;
|
||
animation: treasureFloat 2s ease-out forwards;
|
||
${glowEffect}
|
||
`;
|
||
|
||
// Position at tunnel
|
||
const tunnelElement = document.getElementById(`${digger}-tunnel`);
|
||
if (tunnelElement) {
|
||
const rect = tunnelElement.getBoundingClientRect();
|
||
treasureElement.style.left = (rect.left + rect.width / 2) + 'px';
|
||
treasureElement.style.top = rect.top + 'px';
|
||
|
||
document.body.appendChild(treasureElement);
|
||
|
||
// Show discovery message
|
||
setTimeout(() => {
|
||
if (digger === 'player') {
|
||
this.showTreasureMessage(`🦊 ${memory}`);
|
||
this.playSound('milestone', 0.25);
|
||
}
|
||
}, 200);
|
||
|
||
// Remove treasure element
|
||
setTimeout(() => {
|
||
if (treasureElement.parentNode) {
|
||
treasureElement.parentNode.removeChild(treasureElement);
|
||
}
|
||
}, 2000);
|
||
}
|
||
}
|
||
|
||
showTreasureMessage(message) {
|
||
const treasureMsg = document.createElement('div');
|
||
treasureMsg.className = 'treasure-message';
|
||
treasureMsg.textContent = message;
|
||
treasureMsg.style.cssText = `
|
||
position: fixed;
|
||
top: 15%;
|
||
right: 20px;
|
||
background: rgba(255, 215, 0, 0.95);
|
||
color: #333;
|
||
padding: 12px 18px;
|
||
border-radius: 20px;
|
||
font-size: 0.9rem;
|
||
font-weight: 600;
|
||
box-shadow: 0 6px 20px rgba(255, 215, 0, 0.4);
|
||
z-index: 9999;
|
||
animation: treasureSlideIn 1.5s ease-out forwards;
|
||
max-width: 200px;
|
||
text-align: center;
|
||
`;
|
||
|
||
document.body.appendChild(treasureMsg);
|
||
|
||
// Remove after display
|
||
setTimeout(() => {
|
||
if (treasureMsg.parentNode) {
|
||
treasureMsg.parentNode.removeChild(treasureMsg);
|
||
}
|
||
}, 1500);
|
||
}
|
||
|
||
startAIRacers() {
|
||
if (this.aiUpdateInterval) {
|
||
clearInterval(this.aiUpdateInterval);
|
||
}
|
||
|
||
this.aiUpdateInterval = setInterval(() => {
|
||
if (!this.isGameActive) return;
|
||
|
||
this.aiRacers.forEach(ai => {
|
||
// Store previous position for passing detection
|
||
ai.previousPosition = ai.position;
|
||
|
||
// AI progress based on their speed and some randomness
|
||
let baseProgress = ai.speed * (Math.random() * 0.8 + 0.6); // Some variance
|
||
|
||
// Rubber band catchup system - AI speeds up when behind player
|
||
const playerProgress = this.correctAnswers;
|
||
const gapBehindPlayer = playerProgress - ai.position;
|
||
|
||
if (gapBehindPlayer > 0) {
|
||
// AI is behind - apply catchup boost
|
||
let catchupMultiplier = 1.0;
|
||
|
||
if (gapBehindPlayer >= 8) {
|
||
catchupMultiplier = 2.5; // Massive catchup for big gaps
|
||
} else if (gapBehindPlayer >= 5) {
|
||
catchupMultiplier = 2.0; // Strong catchup for medium gaps
|
||
} else if (gapBehindPlayer >= 3) {
|
||
catchupMultiplier = 1.6; // Moderate catchup for small gaps
|
||
} else if (gapBehindPlayer >= 1) {
|
||
catchupMultiplier = 1.3; // Light catchup for tiny gaps
|
||
}
|
||
|
||
// Apply the catchup boost
|
||
baseProgress *= catchupMultiplier;
|
||
|
||
// Add extra urgency if player is on a streak
|
||
if (this.streak >= 5) {
|
||
baseProgress *= 1.3; // AI gets desperate when player is hot
|
||
}
|
||
|
||
// Debug logging for catchup
|
||
if (gapBehindPlayer >= 3 && this.totalQuestions % 3 === 0) {
|
||
console.log(`🏃 ${ai.name} Catchup: Gap=${Math.round(gapBehindPlayer)} Boost=${Math.round(catchupMultiplier * 100)}% Progress=${Math.round(baseProgress * 100)/100}`);
|
||
}
|
||
}
|
||
|
||
ai.position += baseProgress;
|
||
|
||
// Calculate visual progress
|
||
let visualProgress;
|
||
|
||
if (this.style === 'practice') {
|
||
// Traditional race to finish line
|
||
const progress = (ai.position / this.raceGoal) * 100;
|
||
visualProgress = Math.min(progress, 100);
|
||
} else {
|
||
// Sprint and survival: visual progress based on position relative to reasonable target
|
||
const visualTarget = 30;
|
||
const progress = (ai.position / visualTarget) * 100;
|
||
visualProgress = Math.min(progress, 95); // Cap at 95% so no one "wins" visually
|
||
}
|
||
|
||
const aiElement = document.getElementById(ai.id);
|
||
|
||
if (aiElement) {
|
||
if (this.style === 'survival') {
|
||
// Use circular positioning for survival mode and track laps
|
||
const lapInfo = this.updateCircularPosition(aiElement, ai.position);
|
||
|
||
// Update AI's lap count
|
||
const previousLap = this.aiLaps.get(ai.id) || 0;
|
||
if (lapInfo.lap > previousLap) {
|
||
this.aiLaps.set(ai.id, lapInfo.lap);
|
||
}
|
||
} else if (this.style === 'sprint') {
|
||
// Use tunnel digging for sprint mode
|
||
const aiNumber = ai.id.includes('1') ? '1' : '2';
|
||
const targetTunnel = `ai-tunnel-${aiNumber}`;
|
||
const aiDepth = Math.floor(ai.position);
|
||
console.log(`🦝 ${ai.name} (ID: ${ai.id}) digging tunnel ${targetTunnel} to depth ${aiDepth}`);
|
||
this.updateTunnelDepth(targetTunnel, aiDepth);
|
||
} else {
|
||
// Use linear positioning for practice mode
|
||
aiElement.style.left = visualProgress + '%';
|
||
}
|
||
}
|
||
|
||
// Check if AI wins - only for practice mode with finish line
|
||
if (this.style === 'practice' && visualProgress >= 100 && this.isGameActive) {
|
||
this.handleRaceWin('ai', ai.name);
|
||
}
|
||
});
|
||
}, 1200); // Update AI every 1200ms (slower for balance)
|
||
}
|
||
|
||
handleRaceWin(winner, aiName = null) {
|
||
if (!this.isGameActive) return;
|
||
|
||
// Store the race winner for medal calculation
|
||
this.raceWinner = winner;
|
||
this.winningAI = aiName;
|
||
|
||
// Stop all race activity
|
||
if (this.aiUpdateInterval) {
|
||
clearInterval(this.aiUpdateInterval);
|
||
this.aiUpdateInterval = null;
|
||
}
|
||
|
||
const winnerElement = winner === 'player'
|
||
? document.getElementById('player-racer')
|
||
: document.getElementById(this.aiRacers.find(ai => ai.name === aiName)?.id);
|
||
|
||
if (winnerElement) {
|
||
winnerElement.classList.add('winner');
|
||
winnerElement.style.left = '95%'; // Move to finish line
|
||
}
|
||
|
||
// Play celebration sound and show message
|
||
if (winner === 'player') {
|
||
this.playSound('celebration');
|
||
setTimeout(() => {
|
||
// Check if we're in endless steam journey mode
|
||
const isEndlessSteamJourney = document.querySelector('.route-path');
|
||
if (isEndlessSteamJourney) {
|
||
console.log('🚂 AI race completion detected but endless mode active - route progression will handle');
|
||
return;
|
||
}
|
||
this.endRace();
|
||
}, 1500);
|
||
} else {
|
||
this.playSound('gameOver');
|
||
setTimeout(() => {
|
||
// Check if we're in endless steam journey mode
|
||
const isEndlessSteamJourney = document.querySelector('.route-path');
|
||
if (isEndlessSteamJourney) {
|
||
console.log('🚂 AI race completion detected but endless mode active - route progression will handle');
|
||
return;
|
||
}
|
||
// Show "try again" modal or end game
|
||
alert(`🏁 ${aiName} wins the race! You got ${this.correctAnswers}/${this.raceGoal} correct. Great effort!`);
|
||
this.endRace();
|
||
}, 800);
|
||
}
|
||
}
|
||
|
||
stopRace() {
|
||
if (this.aiUpdateInterval) {
|
||
clearInterval(this.aiUpdateInterval);
|
||
this.aiUpdateInterval = null;
|
||
}
|
||
|
||
// Stop all sounds when stopping race
|
||
this.stopAllSounds();
|
||
}
|
||
|
||
playSound(type, volume = 0.15) {
|
||
try {
|
||
const audioContext = new (window.AudioContext || window.webkitAudioContext)();
|
||
|
||
// Track audio contexts for cleanup
|
||
if (!window.audioContexts) {
|
||
window.audioContexts = [];
|
||
}
|
||
window.audioContexts.push(audioContext);
|
||
|
||
if (type === 'correct') {
|
||
// Classic 90s "power-up" sound - ascending beeps
|
||
this.play90sSound(audioContext, [
|
||
{freq: 523, time: 0, duration: 0.08}, // C5
|
||
{freq: 659, time: 0.08, duration: 0.08}, // E5
|
||
{freq: 784, time: 0.16, duration: 0.12} // G5
|
||
], volume, 'sawtooth');
|
||
|
||
} else if (type === 'incorrect') {
|
||
// Classic arcade "error" sound - descending buzz
|
||
this.play90sSound(audioContext, [
|
||
{freq: 400, time: 0, duration: 0.15},
|
||
{freq: 300, time: 0.05, duration: 0.15},
|
||
{freq: 200, time: 0.1, duration: 0.2}
|
||
], volume * 0.8, 'square');
|
||
|
||
} else if (type === 'timeout') {
|
||
// Classic "time's up" alarm
|
||
this.play90sSound(audioContext, [
|
||
{freq: 800, time: 0, duration: 0.1},
|
||
{freq: 600, time: 0.1, duration: 0.1},
|
||
{freq: 800, time: 0.2, duration: 0.1},
|
||
{freq: 600, time: 0.3, duration: 0.15}
|
||
], volume, 'square');
|
||
|
||
} else if (type === 'countdown') {
|
||
// Classic arcade countdown beep
|
||
this.play90sSound(audioContext, [
|
||
{freq: 800, time: 0, duration: 0.15}
|
||
], volume * 0.6, 'sine');
|
||
|
||
} else if (type === 'race_start') {
|
||
// Epic race start fanfare
|
||
this.play90sSound(audioContext, [
|
||
{freq: 523, time: 0, duration: 0.1}, // C5
|
||
{freq: 659, time: 0.1, duration: 0.1}, // E5
|
||
{freq: 784, time: 0.2, duration: 0.1}, // G5
|
||
{freq: 1046, time: 0.3, duration: 0.3} // C6 - triumphant!
|
||
], volume * 1.2, 'sawtooth');
|
||
|
||
} else if (type === 'celebration') {
|
||
// Classic victory fanfare - like completing a level
|
||
this.play90sSound(audioContext, [
|
||
{freq: 523, time: 0, duration: 0.12}, // C5
|
||
{freq: 659, time: 0.12, duration: 0.12}, // E5
|
||
{freq: 784, time: 0.24, duration: 0.12}, // G5
|
||
{freq: 1046, time: 0.36, duration: 0.24}, // C6
|
||
{freq: 1318, time: 0.6, duration: 0.3} // E6 - epic finish!
|
||
], volume * 1.5, 'sawtooth');
|
||
|
||
} else if (type === 'lap_celebration') {
|
||
// Radical "bonus achieved" sound
|
||
this.play90sSound(audioContext, [
|
||
{freq: 1046, time: 0, duration: 0.08}, // C6
|
||
{freq: 1318, time: 0.08, duration: 0.08}, // E6
|
||
{freq: 1568, time: 0.16, duration: 0.08}, // G6
|
||
{freq: 2093, time: 0.24, duration: 0.15} // C7 - totally rad!
|
||
], volume * 1.3, 'sawtooth');
|
||
|
||
} else if (type === 'gameOver') {
|
||
// Classic "game over" descending tones
|
||
this.play90sSound(audioContext, [
|
||
{freq: 400, time: 0, duration: 0.2},
|
||
{freq: 350, time: 0.2, duration: 0.2},
|
||
{freq: 300, time: 0.4, duration: 0.2},
|
||
{freq: 250, time: 0.6, duration: 0.3},
|
||
{freq: 200, time: 0.9, duration: 0.4}
|
||
], volume, 'triangle');
|
||
|
||
} else if (type === 'ai_turbo') {
|
||
// Sound when AI goes into turbo mode
|
||
this.play90sSound(audioContext, [
|
||
{freq: 200, time: 0, duration: 0.05},
|
||
{freq: 400, time: 0.05, duration: 0.05},
|
||
{freq: 600, time: 0.1, duration: 0.05},
|
||
{freq: 800, time: 0.15, duration: 0.1}
|
||
], volume * 0.7, 'sawtooth');
|
||
|
||
} else if (type === 'milestone') {
|
||
// Rad milestone sound - like collecting a power-up
|
||
this.play90sSound(audioContext, [
|
||
{freq: 659, time: 0, duration: 0.1}, // E5
|
||
{freq: 784, time: 0.1, duration: 0.1}, // G5
|
||
{freq: 880, time: 0.2, duration: 0.1}, // A5
|
||
{freq: 1046, time: 0.3, duration: 0.15} // C6 - awesome!
|
||
], volume * 1.1, 'sawtooth');
|
||
|
||
} else if (type === 'streak') {
|
||
// Epic streak sound - getting hot!
|
||
this.play90sSound(audioContext, [
|
||
{freq: 880, time: 0, duration: 0.06}, // A5
|
||
{freq: 1046, time: 0.06, duration: 0.06}, // C6
|
||
{freq: 1318, time: 0.12, duration: 0.08}, // E6
|
||
{freq: 1760, time: 0.2, duration: 0.1} // A6 - on fire!
|
||
], volume * 1.2, 'sawtooth');
|
||
|
||
} else if (type === 'combo') {
|
||
// Gnarly combo sound - for rapid correct answers
|
||
this.play90sSound(audioContext, [
|
||
{freq: 1046, time: 0, duration: 0.04}, // C6
|
||
{freq: 1175, time: 0.04, duration: 0.04}, // D6
|
||
{freq: 1318, time: 0.08, duration: 0.04}, // E6
|
||
{freq: 1480, time: 0.12, duration: 0.06} // F#6
|
||
], volume * 0.9, 'square');
|
||
|
||
} else if (type === 'whoosh') {
|
||
// Cool whoosh sound for fast responses
|
||
const whooshOsc = audioContext.createOscillator();
|
||
const whooshGain = audioContext.createGain();
|
||
const whooshFilter = audioContext.createBiquadFilter();
|
||
|
||
whooshOsc.connect(whooshFilter);
|
||
whooshFilter.connect(whooshGain);
|
||
whooshGain.connect(audioContext.destination);
|
||
|
||
whooshOsc.type = 'sawtooth';
|
||
whooshFilter.type = 'highpass';
|
||
whooshFilter.frequency.setValueAtTime(1000, audioContext.currentTime);
|
||
whooshFilter.frequency.exponentialRampToValueAtTime(100, audioContext.currentTime + 0.3);
|
||
|
||
whooshOsc.frequency.setValueAtTime(400, audioContext.currentTime);
|
||
whooshOsc.frequency.exponentialRampToValueAtTime(800, audioContext.currentTime + 0.15);
|
||
whooshOsc.frequency.exponentialRampToValueAtTime(200, audioContext.currentTime + 0.3);
|
||
|
||
whooshGain.gain.setValueAtTime(0, audioContext.currentTime);
|
||
whooshGain.gain.exponentialRampToValueAtTime(volume * 0.6, audioContext.currentTime + 0.02);
|
||
whooshGain.gain.exponentialRampToValueAtTime(0.001, audioContext.currentTime + 0.3);
|
||
|
||
whooshOsc.start(audioContext.currentTime);
|
||
whooshOsc.stop(audioContext.currentTime + 0.3);
|
||
|
||
} else if (type === 'train_chuff') {
|
||
// Realistic steam train chuffing sound
|
||
const chuffOsc = audioContext.createOscillator();
|
||
const chuffGain = audioContext.createGain();
|
||
const chuffFilter = audioContext.createBiquadFilter();
|
||
|
||
chuffOsc.connect(chuffFilter);
|
||
chuffFilter.connect(chuffGain);
|
||
chuffGain.connect(audioContext.destination);
|
||
|
||
chuffOsc.type = 'sawtooth';
|
||
chuffFilter.type = 'bandpass';
|
||
chuffFilter.frequency.setValueAtTime(150, audioContext.currentTime);
|
||
chuffFilter.Q.setValueAtTime(5, audioContext.currentTime);
|
||
|
||
chuffOsc.frequency.setValueAtTime(80, audioContext.currentTime);
|
||
chuffOsc.frequency.exponentialRampToValueAtTime(120, audioContext.currentTime + 0.05);
|
||
chuffOsc.frequency.exponentialRampToValueAtTime(60, audioContext.currentTime + 0.2);
|
||
|
||
chuffGain.gain.setValueAtTime(0, audioContext.currentTime);
|
||
chuffGain.gain.exponentialRampToValueAtTime(volume * 0.8, audioContext.currentTime + 0.01);
|
||
chuffGain.gain.exponentialRampToValueAtTime(0.001, audioContext.currentTime + 0.2);
|
||
|
||
chuffOsc.start(audioContext.currentTime);
|
||
chuffOsc.stop(audioContext.currentTime + 0.2);
|
||
|
||
} else if (type === 'train_whistle') {
|
||
// Classic steam train whistle
|
||
this.play90sSound(audioContext, [
|
||
{freq: 523, time: 0, duration: 0.3}, // C5 - long whistle
|
||
{freq: 659, time: 0.1, duration: 0.4}, // E5 - harmony
|
||
{freq: 523, time: 0.3, duration: 0.2} // C5 - fade out
|
||
], volume * 1.2, 'sine');
|
||
|
||
} else if (type === 'coal_spill') {
|
||
// Coal chunks spilling sound effect
|
||
const coalOsc = audioContext.createOscillator();
|
||
const coalGain = audioContext.createGain();
|
||
const coalFilter = audioContext.createBiquadFilter();
|
||
|
||
coalOsc.connect(coalFilter);
|
||
coalFilter.connect(coalGain);
|
||
coalGain.connect(audioContext.destination);
|
||
|
||
coalOsc.type = 'square';
|
||
coalFilter.type = 'lowpass';
|
||
coalFilter.frequency.setValueAtTime(300, audioContext.currentTime);
|
||
|
||
// Simulate coal chunks falling with random frequency bursts
|
||
coalOsc.frequency.setValueAtTime(200 + Math.random() * 100, audioContext.currentTime);
|
||
coalOsc.frequency.exponentialRampToValueAtTime(100 + Math.random() * 50, audioContext.currentTime + 0.1);
|
||
coalOsc.frequency.exponentialRampToValueAtTime(80 + Math.random() * 40, audioContext.currentTime + 0.3);
|
||
|
||
coalGain.gain.setValueAtTime(0, audioContext.currentTime);
|
||
coalGain.gain.exponentialRampToValueAtTime(volume * 0.6, audioContext.currentTime + 0.01);
|
||
coalGain.gain.exponentialRampToValueAtTime(volume * 0.3, audioContext.currentTime + 0.15);
|
||
coalGain.gain.exponentialRampToValueAtTime(0.001, audioContext.currentTime + 0.4);
|
||
|
||
coalOsc.start(audioContext.currentTime);
|
||
coalOsc.stop(audioContext.currentTime + 0.4);
|
||
|
||
} else if (type === 'steam_hiss') {
|
||
// Steam hissing sound for locomotive
|
||
const steamOsc = audioContext.createOscillator();
|
||
const steamGain = audioContext.createGain();
|
||
const steamFilter = audioContext.createBiquadFilter();
|
||
|
||
steamOsc.connect(steamFilter);
|
||
steamFilter.connect(steamGain);
|
||
steamGain.connect(audioContext.destination);
|
||
|
||
steamOsc.type = 'triangle';
|
||
steamFilter.type = 'highpass';
|
||
steamFilter.frequency.setValueAtTime(2000, audioContext.currentTime);
|
||
|
||
steamOsc.frequency.setValueAtTime(4000 + Math.random() * 1000, audioContext.currentTime);
|
||
|
||
steamGain.gain.setValueAtTime(0, audioContext.currentTime);
|
||
steamGain.gain.exponentialRampToValueAtTime(volume * 0.4, audioContext.currentTime + 0.02);
|
||
steamGain.gain.exponentialRampToValueAtTime(0.001, audioContext.currentTime + 0.6);
|
||
|
||
steamOsc.start(audioContext.currentTime);
|
||
steamOsc.stop(audioContext.currentTime + 0.6);
|
||
}
|
||
|
||
} catch (e) {
|
||
console.log('🎵 Web Audio not supported - missing out on rad 90s sounds!');
|
||
}
|
||
}
|
||
|
||
play90sSound(audioContext, notes, volume = 0.15, waveType = 'sine') {
|
||
notes.forEach(note => {
|
||
const oscillator = audioContext.createOscillator();
|
||
const gainNode = audioContext.createGain();
|
||
const filterNode = audioContext.createBiquadFilter();
|
||
|
||
// Create that classic 90s arcade sound chain
|
||
oscillator.connect(filterNode);
|
||
filterNode.connect(gainNode);
|
||
gainNode.connect(audioContext.destination);
|
||
|
||
// Set wave type for that retro flavor
|
||
oscillator.type = waveType;
|
||
|
||
// Add some 90s-style filtering
|
||
filterNode.type = 'lowpass';
|
||
filterNode.frequency.setValueAtTime(2000, audioContext.currentTime + note.time);
|
||
filterNode.Q.setValueAtTime(1, audioContext.currentTime + note.time);
|
||
|
||
// Set frequency and add vibrato for that classic arcade wobble
|
||
oscillator.frequency.setValueAtTime(note.freq, audioContext.currentTime + note.time);
|
||
if (waveType === 'sawtooth' || waveType === 'square') {
|
||
// Add slight vibrato for extra 90s flavor
|
||
oscillator.frequency.exponentialRampToValueAtTime(
|
||
note.freq * 1.02,
|
||
audioContext.currentTime + note.time + note.duration * 0.5
|
||
);
|
||
oscillator.frequency.exponentialRampToValueAtTime(
|
||
note.freq,
|
||
audioContext.currentTime + note.time + note.duration
|
||
);
|
||
}
|
||
|
||
// Classic arcade envelope - quick attack, moderate decay
|
||
gainNode.gain.setValueAtTime(0, audioContext.currentTime + note.time);
|
||
gainNode.gain.exponentialRampToValueAtTime(volume, audioContext.currentTime + note.time + 0.01);
|
||
gainNode.gain.exponentialRampToValueAtTime(volume * 0.7, audioContext.currentTime + note.time + note.duration * 0.7);
|
||
gainNode.gain.exponentialRampToValueAtTime(0.001, audioContext.currentTime + note.time + note.duration);
|
||
|
||
oscillator.start(audioContext.currentTime + note.time);
|
||
oscillator.stop(audioContext.currentTime + note.time + note.duration);
|
||
});
|
||
}
|
||
|
||
// ===== ADAPTIVE DIFFICULTY SYSTEM =====
|
||
|
||
trackPerformance(isCorrect) {
|
||
const responseTime = Date.now() - this.questionStartTime;
|
||
const pairKey = `${this.currentNumber}_${this.correctAnswer}_${this.targetSum}`;
|
||
|
||
// Get or create performance data for this pair
|
||
let pairData = this.difficultyTracker.pairPerformance.get(pairKey) || {
|
||
attempts: 0,
|
||
correct: 0,
|
||
totalTime: 0,
|
||
avgTime: 0,
|
||
difficulty: 1,
|
||
recentPerformance: [] // Track last 5 attempts
|
||
};
|
||
|
||
// Update performance data
|
||
pairData.attempts++;
|
||
if (isCorrect) pairData.correct++;
|
||
pairData.totalTime += responseTime;
|
||
pairData.avgTime = pairData.totalTime / pairData.attempts;
|
||
|
||
// Track recent performance (sliding window of 5)
|
||
pairData.recentPerformance.push({
|
||
correct: isCorrect,
|
||
time: responseTime,
|
||
timestamp: Date.now()
|
||
});
|
||
|
||
if (pairData.recentPerformance.length > 5) {
|
||
pairData.recentPerformance.shift();
|
||
}
|
||
|
||
// Calculate dynamic difficulty for this pair
|
||
this.calculatePairDifficulty(pairData);
|
||
|
||
// Update global tracking
|
||
if (isCorrect) {
|
||
this.difficultyTracker.consecutiveCorrect++;
|
||
this.difficultyTracker.consecutiveIncorrect = 0;
|
||
} else {
|
||
this.difficultyTracker.consecutiveIncorrect++;
|
||
this.difficultyTracker.consecutiveCorrect = 0;
|
||
}
|
||
|
||
// Adapt overall difficulty
|
||
this.adaptGlobalDifficulty();
|
||
|
||
// Adapt AI speeds based on player performance
|
||
this.adaptAISpeeds();
|
||
|
||
// Store updated data
|
||
this.difficultyTracker.pairPerformance.set(pairKey, pairData);
|
||
|
||
// Exit learning mode after sufficient data
|
||
if (this.difficultyTracker.pairPerformance.size >= 5 &&
|
||
Array.from(this.difficultyTracker.pairPerformance.values())
|
||
.some(data => data.attempts >= 3)) {
|
||
this.difficultyTracker.learningMode = false;
|
||
}
|
||
}
|
||
|
||
calculatePairDifficulty(pairData) {
|
||
if (pairData.attempts < 2) return; // Need more data
|
||
|
||
const accuracyRate = pairData.correct / pairData.attempts;
|
||
const avgTime = pairData.avgTime;
|
||
const targetTime = 2000; // Target 2 seconds
|
||
|
||
// Recent performance weight (last 5 attempts matter more)
|
||
const recentAccuracy = pairData.recentPerformance.length > 0 ?
|
||
pairData.recentPerformance.filter(p => p.correct).length / pairData.recentPerformance.length :
|
||
accuracyRate;
|
||
|
||
const recentAvgTime = pairData.recentPerformance.length > 0 ?
|
||
pairData.recentPerformance.reduce((sum, p) => sum + p.time, 0) / pairData.recentPerformance.length :
|
||
avgTime;
|
||
|
||
// Calculate difficulty (1-5 scale)
|
||
let newDifficulty = pairData.difficulty;
|
||
|
||
if (recentAccuracy >= 0.8 && recentAvgTime < targetTime) {
|
||
// Performing well - increase difficulty
|
||
newDifficulty = Math.min(5, pairData.difficulty + 0.2);
|
||
} else if (recentAccuracy < 0.5 || recentAvgTime > targetTime * 1.5) {
|
||
// Struggling - decrease difficulty
|
||
newDifficulty = Math.max(1, pairData.difficulty - 0.3);
|
||
}
|
||
|
||
pairData.difficulty = newDifficulty;
|
||
}
|
||
|
||
adaptGlobalDifficulty() {
|
||
const tracker = this.difficultyTracker;
|
||
|
||
// Adapt time limits based on recent performance
|
||
if (tracker.consecutiveCorrect >= 3) {
|
||
// Reduce time limit (increase difficulty)
|
||
tracker.currentTimeLimit = Math.max(1000,
|
||
tracker.currentTimeLimit - (tracker.currentTimeLimit * tracker.adaptationRate));
|
||
} else if (tracker.consecutiveIncorrect >= 2) {
|
||
// Increase time limit (decrease difficulty)
|
||
tracker.currentTimeLimit = Math.min(5000,
|
||
tracker.currentTimeLimit + (tracker.baseTimeLimit * tracker.adaptationRate));
|
||
}
|
||
|
||
// Update overall difficulty level
|
||
const avgDifficulty = Array.from(tracker.pairPerformance.values())
|
||
.reduce((sum, data) => sum + data.difficulty, 0) /
|
||
Math.max(1, tracker.pairPerformance.size);
|
||
|
||
tracker.difficultyLevel = Math.round(avgDifficulty);
|
||
}
|
||
|
||
adaptAISpeeds() {
|
||
// Don't adapt during learning mode - let player get comfortable first
|
||
if (this.difficultyTracker.learningMode) return;
|
||
|
||
const tracker = this.difficultyTracker;
|
||
const playerSuccessRate = this.calculateRecentSuccessRate();
|
||
const avgResponseTime = this.calculateAverageResponseTime();
|
||
|
||
// Base speed multipliers for each race mode
|
||
let baseSpeedMultiplier;
|
||
switch (this.style) {
|
||
case 'practice': baseSpeedMultiplier = 0.7; break;
|
||
case 'sprint': baseSpeedMultiplier = 0.9; break;
|
||
case 'survival': baseSpeedMultiplier = this.speedMultiplier * (this.survivalMultiplier || 1.0); break;
|
||
default: baseSpeedMultiplier = 0.7;
|
||
}
|
||
|
||
// Calculate adaptive multiplier based on player performance
|
||
let adaptiveMultiplier = 1.0;
|
||
|
||
// Success rate factor (0.5x to 1.8x based on 40%-95% success rate)
|
||
if (playerSuccessRate > 0.85) {
|
||
adaptiveMultiplier *= 1.6; // Player doing great - speed up AI significantly
|
||
} else if (playerSuccessRate > 0.75) {
|
||
adaptiveMultiplier *= 1.3; // Player doing well - speed up AI moderately
|
||
} else if (playerSuccessRate > 0.60) {
|
||
adaptiveMultiplier *= 1.0; // Player doing okay - keep AI at base speed
|
||
} else if (playerSuccessRate > 0.45) {
|
||
adaptiveMultiplier *= 0.75; // Player struggling - slow down AI
|
||
} else {
|
||
adaptiveMultiplier *= 0.5; // Player really struggling - significantly slow AI
|
||
}
|
||
|
||
// Response time factor - faster players get faster AI
|
||
if (avgResponseTime < 1500) {
|
||
adaptiveMultiplier *= 1.2; // Very fast player
|
||
} else if (avgResponseTime < 2500) {
|
||
adaptiveMultiplier *= 1.1; // Fast player
|
||
} else if (avgResponseTime > 4000) {
|
||
adaptiveMultiplier *= 0.9; // Slow player
|
||
}
|
||
|
||
// Streak bonus - players on hot streaks get more challenge
|
||
if (this.streak >= 8) {
|
||
adaptiveMultiplier *= 1.3;
|
||
} else if (this.streak >= 5) {
|
||
adaptiveMultiplier *= 1.15;
|
||
}
|
||
|
||
// Apply bounds to prevent extreme values
|
||
adaptiveMultiplier = Math.max(0.3, Math.min(2.0, adaptiveMultiplier));
|
||
|
||
// Update AI speeds with adaptive multiplier
|
||
const finalSpeedMultiplier = baseSpeedMultiplier * adaptiveMultiplier;
|
||
|
||
if (this.aiRacers && this.aiRacers.length >= 2) {
|
||
// Swift AI (more aggressive, varies more)
|
||
this.aiRacers[0].speed = 0.25 * finalSpeedMultiplier;
|
||
// Math Bot (more consistent, varies less)
|
||
this.aiRacers[1].speed = 0.15 * finalSpeedMultiplier;
|
||
|
||
// Show player feedback for significant AI speed changes
|
||
this.showAIAdaptationFeedback(adaptiveMultiplier, playerSuccessRate);
|
||
|
||
// Debug logging for AI adaptation
|
||
if (this.totalQuestions % 5 === 0) {
|
||
console.log('🤖 AI Speed Adaptation:', {
|
||
playerSuccessRate: Math.round(playerSuccessRate * 100) + '%',
|
||
avgResponseTime: Math.round(avgResponseTime) + 'ms',
|
||
streak: this.streak,
|
||
adaptiveMultiplier: Math.round(adaptiveMultiplier * 100) / 100,
|
||
swiftAISpeed: Math.round(this.aiRacers[0].speed * 1000) / 1000,
|
||
mathBotSpeed: Math.round(this.aiRacers[1].speed * 1000) / 1000
|
||
});
|
||
}
|
||
}
|
||
}
|
||
|
||
calculateRecentSuccessRate() {
|
||
// Calculate success rate from last 10 questions, or all questions if less than 10
|
||
const recentQuestions = Math.min(10, this.totalQuestions);
|
||
if (recentQuestions === 0) return 0.5; // Default for first question
|
||
|
||
// Use global tracking for recent performance
|
||
const recentCorrect = Math.max(0, this.correctAnswers - Math.max(0, this.totalQuestions - recentQuestions));
|
||
return recentCorrect / recentQuestions;
|
||
}
|
||
|
||
calculateAverageResponseTime() {
|
||
// Calculate average response time from recent pair performance data
|
||
const recentPairs = Array.from(this.difficultyTracker.pairPerformance.values())
|
||
.filter(data => data.attempts >= 1)
|
||
.slice(-5); // Last 5 different pairs encountered
|
||
|
||
if (recentPairs.length === 0) return 3000; // Default for learning mode
|
||
|
||
const totalTime = recentPairs.reduce((sum, data) => sum + data.avgTime, 0);
|
||
return totalTime / recentPairs.length;
|
||
}
|
||
|
||
showAIAdaptationFeedback(adaptiveMultiplier, playerSuccessRate) {
|
||
// Only show feedback for significant changes and not too frequently
|
||
if (this.totalQuestions < 8) return; // Wait for some data
|
||
if (this.totalQuestions % 7 !== 0) return; // Don't show too often
|
||
|
||
let message = '';
|
||
let type = '';
|
||
|
||
if (adaptiveMultiplier >= 1.4) {
|
||
const messages = [
|
||
"🔥 You're crushing it! AI opponents stepping up their game!",
|
||
"⚡ Impressive speed! The AI is now in competition mode!",
|
||
"🚀 You're on fire! AI racers are feeling the pressure!",
|
||
"💪 Amazing performance! AI opponents are now racing seriously!"
|
||
];
|
||
message = messages[Math.floor(Math.random() * messages.length)];
|
||
type = 'ai_speeding_up';
|
||
} else if (adaptiveMultiplier <= 0.7) {
|
||
const messages = [
|
||
"🤗 Take your time! AI opponents are giving you space to learn!",
|
||
"📚 Learning mode active! AI racers are being more patient!",
|
||
"🎯 No rush! AI opponents are matching your learning pace!",
|
||
"🌟 Perfect! AI racers are helping you build confidence!"
|
||
];
|
||
message = messages[Math.floor(Math.random() * messages.length)];
|
||
type = 'ai_slowing_down';
|
||
}
|
||
|
||
if (message) {
|
||
this.showAdaptiveFeedback(message, type);
|
||
}
|
||
}
|
||
|
||
getAdaptiveTimeLimit() {
|
||
// Get base adaptive time limit
|
||
let adaptiveTime;
|
||
|
||
if (this.difficultyTracker.learningMode) {
|
||
adaptiveTime = Math.max(2000, this.difficultyTracker.currentTimeLimit);
|
||
} else {
|
||
const pairKey = `${this.currentNumber}_${this.correctAnswer}_${this.targetSum}`;
|
||
const pairData = this.difficultyTracker.pairPerformance.get(pairKey);
|
||
|
||
if (pairData && pairData.attempts >= 2) {
|
||
// Use pair-specific difficulty
|
||
const baseTime = this.difficultyTracker.baseTimeLimit;
|
||
const difficultyMultiplier = (6 - pairData.difficulty) / 5; // Invert: difficulty 1 = more time
|
||
adaptiveTime = Math.max(1000, baseTime * difficultyMultiplier);
|
||
} else {
|
||
// Default for new pairs
|
||
adaptiveTime = this.difficultyTracker.currentTimeLimit;
|
||
}
|
||
}
|
||
|
||
// Apply user timeout setting override
|
||
return this.applyTimeoutSetting(adaptiveTime);
|
||
}
|
||
|
||
applyTimeoutSetting(baseTime) {
|
||
// Apply timeout setting multiplier
|
||
switch (this.timeoutSetting) {
|
||
case 'preschool':
|
||
return Math.max(baseTime * 4, 20000); // At least 20 seconds - perfect for 4-year-olds!
|
||
case 'kindergarten':
|
||
return Math.max(baseTime * 3, 15000); // At least 15 seconds - great for 5-6 year olds
|
||
case 'relaxed':
|
||
return Math.max(baseTime * 2.4, 12000); // At least 12 seconds - comfortable pace
|
||
case 'slow':
|
||
return Math.max(baseTime * 1.6, 8000); // At least 8 seconds, or 60% longer
|
||
case 'normal':
|
||
return Math.max(baseTime, 5000); // At least 5 seconds (default)
|
||
case 'fast':
|
||
return Math.max(baseTime * 0.6, 3000); // At least 3 seconds, or 40% shorter
|
||
case 'expert':
|
||
return Math.max(baseTime * 0.4, 2000); // At least 2 seconds, or 60% shorter
|
||
default:
|
||
return baseTime;
|
||
}
|
||
}
|
||
|
||
getDifficultyInsights() {
|
||
const tracker = this.difficultyTracker;
|
||
const insights = {
|
||
overallLevel: tracker.difficultyLevel,
|
||
currentTimeLimit: tracker.currentTimeLimit,
|
||
learningMode: tracker.learningMode,
|
||
strugglingPairs: [],
|
||
masteredPairs: [],
|
||
totalPairsTested: tracker.pairPerformance.size
|
||
};
|
||
|
||
// Analyze pair-specific performance
|
||
for (const [pairKey, data] of tracker.pairPerformance) {
|
||
const [num1, num2, sum] = pairKey.split('_').map(Number);
|
||
const accuracy = data.correct / data.attempts;
|
||
|
||
if (accuracy < 0.6 && data.attempts >= 3) {
|
||
insights.strugglingPairs.push({
|
||
pair: `${num1} + ${num2} = ${sum}`,
|
||
accuracy: Math.round(accuracy * 100),
|
||
avgTime: Math.round(data.avgTime),
|
||
difficulty: data.difficulty
|
||
});
|
||
} else if (accuracy >= 0.9 && data.avgTime < 1500 && data.attempts >= 5) {
|
||
insights.masteredPairs.push({
|
||
pair: `${num1} + ${num2} = ${sum}`,
|
||
accuracy: Math.round(accuracy * 100),
|
||
avgTime: Math.round(data.avgTime),
|
||
difficulty: data.difficulty
|
||
});
|
||
}
|
||
}
|
||
|
||
return insights;
|
||
}
|
||
}
|
||
|
||
// Particle Effects System
|
||
class ParticleSystem {
|
||
constructor() {
|
||
this.container = document.getElementById('particle-container');
|
||
this.particles = [];
|
||
}
|
||
|
||
createParticle(x, y, type = 'star', animation = 'burst') {
|
||
const particle = document.createElement('div');
|
||
particle.className = `particle ${type} ${animation}`;
|
||
|
||
// Random size between 8-20px
|
||
const size = 8 + Math.random() * 12;
|
||
particle.style.width = size + 'px';
|
||
particle.style.height = size + 'px';
|
||
|
||
// Position
|
||
particle.style.left = x + 'px';
|
||
particle.style.top = y + 'px';
|
||
|
||
// Add random velocity for trail effects
|
||
if (animation === 'trail') {
|
||
const dx = (Math.random() - 0.5) * 200;
|
||
const dy = (Math.random() - 0.5) * 200;
|
||
particle.style.setProperty('--dx', dx + 'px');
|
||
particle.style.setProperty('--dy', dy + 'px');
|
||
}
|
||
|
||
this.container.appendChild(particle);
|
||
this.particles.push(particle);
|
||
|
||
// Remove particle after animation
|
||
const duration = animation === 'floating' ? 2000 : animation === 'firework' ? 1200 : 800;
|
||
setTimeout(() => this.removeParticle(particle), duration);
|
||
}
|
||
|
||
removeParticle(particle) {
|
||
if (particle.parentNode) {
|
||
particle.parentNode.removeChild(particle);
|
||
}
|
||
this.particles = this.particles.filter(p => p !== particle);
|
||
}
|
||
|
||
// Success celebration - burst of stars
|
||
celebrateSuccess(sourceElement) {
|
||
const rect = sourceElement.getBoundingClientRect();
|
||
const centerX = rect.left + rect.width / 2;
|
||
const centerY = rect.top + rect.height / 2;
|
||
|
||
// Create burst of particles
|
||
for (let i = 0; i < 8; i++) {
|
||
const angle = (i / 8) * Math.PI * 2;
|
||
const distance = 30 + Math.random() * 20;
|
||
const x = centerX + Math.cos(angle) * distance;
|
||
const y = centerY + Math.sin(angle) * distance;
|
||
|
||
const types = ['star', 'circle', 'diamond'];
|
||
const type = types[Math.floor(Math.random() * types.length)];
|
||
|
||
setTimeout(() => {
|
||
this.createParticle(x, y, type, 'burst');
|
||
}, i * 50);
|
||
}
|
||
}
|
||
|
||
// Major celebration - fireworks and confetti
|
||
celebrateMajor(sourceElement) {
|
||
const rect = sourceElement.getBoundingClientRect();
|
||
const centerX = rect.left + rect.width / 2;
|
||
const centerY = rect.top + rect.height / 2;
|
||
|
||
// Central firework
|
||
this.createParticle(centerX, centerY, 'star', 'firework');
|
||
|
||
// Surrounding smaller fireworks
|
||
for (let i = 0; i < 6; i++) {
|
||
const angle = (i / 6) * Math.PI * 2;
|
||
const distance = 80 + Math.random() * 40;
|
||
const x = centerX + Math.cos(angle) * distance;
|
||
const y = centerY + Math.sin(angle) * distance;
|
||
|
||
setTimeout(() => {
|
||
this.createParticle(x, y, 'heart', 'firework');
|
||
}, i * 100);
|
||
}
|
||
|
||
// Confetti shower
|
||
setTimeout(() => {
|
||
for (let i = 0; i < 20; i++) {
|
||
const x = centerX + (Math.random() - 0.5) * 200;
|
||
const y = centerY - 50 + Math.random() * 100;
|
||
|
||
setTimeout(() => {
|
||
this.createParticle(x, y, 'confetti', 'floating');
|
||
}, i * 30);
|
||
}
|
||
}, 300);
|
||
}
|
||
|
||
// Streak celebration - trail effects
|
||
celebrateStreak(sourceElement, streakCount) {
|
||
const rect = sourceElement.getBoundingClientRect();
|
||
const centerX = rect.left + rect.width / 2;
|
||
const centerY = rect.top + rect.height / 2;
|
||
|
||
const particleCount = Math.min(streakCount * 2, 16);
|
||
|
||
for (let i = 0; i < particleCount; i++) {
|
||
const x = centerX + (Math.random() - 0.5) * 100;
|
||
const y = centerY + (Math.random() - 0.5) * 100;
|
||
|
||
setTimeout(() => {
|
||
this.createParticle(x, y, 'circle', 'trail');
|
||
}, i * 25);
|
||
}
|
||
}
|
||
|
||
// Sparkle effect for hovering or highlighting
|
||
sparkle(sourceElement) {
|
||
const rect = sourceElement.getBoundingClientRect();
|
||
const x = rect.left + Math.random() * rect.width;
|
||
const y = rect.top + Math.random() * rect.height;
|
||
|
||
this.createParticle(x, y, 'star', 'sparkle');
|
||
}
|
||
|
||
// Game complete celebration
|
||
celebrateGameComplete(sourceElement) {
|
||
// Multiple waves of celebration
|
||
this.celebrateMajor(sourceElement);
|
||
|
||
setTimeout(() => {
|
||
this.celebrateSuccess(sourceElement);
|
||
}, 400);
|
||
|
||
setTimeout(() => {
|
||
this.celebrateStreak(sourceElement, 10);
|
||
}, 800);
|
||
}
|
||
}
|
||
|
||
// Global particle system instance
|
||
let particleSystem;
|
||
|
||
// Tag filtering system
|
||
function initializeTagFiltering() {
|
||
const tagFilters = document.querySelectorAll('.tag-filter');
|
||
const challengeCards = document.querySelectorAll('.challenge-card');
|
||
|
||
tagFilters.forEach(filter => {
|
||
filter.addEventListener('click', () => {
|
||
// Remove active class from all filters
|
||
tagFilters.forEach(f => f.classList.remove('active'));
|
||
// Add active class to clicked filter
|
||
filter.classList.add('active');
|
||
|
||
const selectedTag = filter.dataset.tag;
|
||
|
||
// Show/hide challenge cards based on selected tag
|
||
challengeCards.forEach(card => {
|
||
if (selectedTag === 'all') {
|
||
card.style.display = 'flex';
|
||
} else {
|
||
const cardTags = card.dataset.tags ? card.dataset.tags.split(',') : [];
|
||
if (cardTags.includes(selectedTag)) {
|
||
card.style.display = 'flex';
|
||
} else {
|
||
card.style.display = 'none';
|
||
}
|
||
}
|
||
});
|
||
|
||
// Update the visible count in the grid
|
||
updateVisibleChallengeLayout();
|
||
});
|
||
});
|
||
}
|
||
|
||
function updateVisibleChallengeLayout() {
|
||
// Force the grid to recalculate layout for visible items
|
||
const challengesGrid = document.querySelector('.challenges-grid');
|
||
if (challengesGrid) {
|
||
challengesGrid.style.display = 'none';
|
||
challengesGrid.offsetHeight; // Force reflow
|
||
challengesGrid.style.display = 'grid';
|
||
}
|
||
}
|
||
|
||
document.addEventListener('DOMContentLoaded', () => {
|
||
// Initialize particle system first
|
||
particleSystem = new ParticleSystem();
|
||
|
||
// Set bead shape class based on data attribute
|
||
const beadSample = document.querySelector('.bead-sample[data-shape]');
|
||
if (beadSample) {
|
||
const shape = beadSample.getAttribute('data-shape').toLowerCase();
|
||
beadSample.classList.add(shape);
|
||
}
|
||
|
||
new ModalManager();
|
||
new SectionNavigator();
|
||
new SorobanQuiz();
|
||
new SortingChallenge();
|
||
new MatchingChallenge();
|
||
new SpeedComplementRace();
|
||
|
||
// Initialize tag filtering system
|
||
initializeTagFiltering();
|
||
|
||
// Intercept print attempts and download PDF instead
|
||
window.addEventListener('beforeprint', (e) => {
|
||
e.preventDefault();
|
||
handlePrintRequest();
|
||
});
|
||
|
||
// Also intercept Ctrl+P / Cmd+P
|
||
document.addEventListener('keydown', (e) => {
|
||
if ((e.ctrlKey || e.metaKey) && e.key === 'p') {
|
||
e.preventDefault();
|
||
handlePrintRequest();
|
||
}
|
||
});
|
||
});
|
||
</script>
|
||
|
||
<!-- Race Countdown Overlay -->
|
||
<div id="race-countdown" class="race-countdown">
|
||
<div class="countdown-display">
|
||
<div id="countdown-text" class="countdown-text">Get Ready!</div>
|
||
<div id="countdown-number" class="countdown-number">3</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Particle Effects Container -->
|
||
<div id="particle-container" class="particle-container"></div>
|
||
</body>
|
||
</html> |