diff --git a/public/redirect2.js b/public/redirect2.js
index 972cb35..ae50a1c 100644
--- a/public/redirect2.js
+++ b/public/redirect2.js
@@ -28,8 +28,8 @@
// Small screens: force mobile
targetPage = 'mobile.html'
} else if (isTabletDevice || (width >= 500 && width < 1200)) {
- // Tablets: iPad/Android tablet OR 500-1200px
- targetPage = 'index.html'
+ // Tablets: iPad/Android tablet OR 500-1200px - use full app
+ targetPage = 'tablet-app.html'
} else {
// Desktop: >= 1200px
targetPage = 'index.html'
diff --git a/public/tablet-app.css b/public/tablet-app.css
new file mode 100644
index 0000000..5d3dc9e
--- /dev/null
+++ b/public/tablet-app.css
@@ -0,0 +1,471 @@
+/* Full-Screen Tablet App */
+* {
+ margin: 0;
+ padding: 0;
+ box-sizing: border-box;
+ -webkit-tap-highlight-color: transparent;
+}
+
+html, body {
+ height: 100%;
+ overflow: hidden;
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Arial, sans-serif;
+ background: #1a202c;
+ touch-action: manipulation;
+ user-select: none;
+}
+
+.app-fullscreen {
+ width: 100vw;
+ height: 100vh;
+ display: flex;
+ flex-direction: column;
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+ overflow: hidden;
+}
+
+/* Header */
+.app-header {
+ background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
+ padding: 10px 15px;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ box-shadow: 0 2px 10px rgba(0,0,0,0.2);
+ flex-shrink: 0;
+}
+
+.icon-btn {
+ background: rgba(255,255,255,0.2);
+ border: none;
+ color: white;
+ width: 44px;
+ height: 44px;
+ border-radius: 10px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-size: 20px;
+ cursor: pointer;
+}
+
+.header-info {
+ flex: 1;
+ text-align: center;
+}
+
+.level-text {
+ color: white;
+ font-size: 18px;
+ font-weight: 700;
+}
+
+.header-stats {
+ display: flex;
+ gap: 10px;
+}
+
+.stat-item {
+ background: rgba(255,255,255,0.2);
+ padding: 8px 12px;
+ border-radius: 20px;
+ color: white;
+ font-weight: 600;
+ display: flex;
+ align-items: center;
+ gap: 6px;
+}
+
+/* Progress Bar */
+.progress-bar-wrapper {
+ background: rgba(255,255,255,0.95);
+ padding: 8px 15px;
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ flex-shrink: 0;
+}
+
+.progress-bar {
+ flex: 1;
+ height: 8px;
+ background: #e9ecef;
+ border-radius: 4px;
+ overflow: hidden;
+}
+
+.progress-fill {
+ height: 100%;
+ background: linear-gradient(90deg, #48bb78, #38a169);
+ width: 0%;
+ transition: width 0.3s ease;
+}
+
+.progress-text {
+ font-size: 14px;
+ font-weight: 600;
+ color: #4a5568;
+ min-width: 50px;
+}
+
+/* Grid Container */
+.grid-container-wrapper {
+ flex: 1;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ padding: 15px;
+ overflow: hidden;
+ background: rgba(255,255,255,0.95);
+}
+
+.crossword-grid {
+ display: grid;
+ background: #a0aec0;
+ padding: 6px;
+ border-radius: 10px;
+ gap: 2px;
+ box-shadow: 0 4px 15px rgba(0,0,0,0.15);
+}
+
+/* Grid Cells */
+.grid-cell {
+ background: white;
+ border: 2px solid #cbd5e0;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ position: relative;
+ font-weight: bold;
+ transition: all 0.2s;
+ cursor: pointer;
+}
+
+.grid-cell.clue-cell {
+ background: linear-gradient(135deg, #4a5568, #2d3748);
+ color: white;
+ padding: 4px;
+ font-size: 10px;
+ font-weight: 600;
+ line-height: 1.2;
+ text-align: left;
+ flex-direction: column;
+ align-items: flex-start;
+ justify-content: flex-start;
+ overflow: hidden;
+ cursor: default;
+ pointer-events: none;
+}
+
+.clue-text {
+ font-size: 10px;
+ line-height: 1.2;
+ word-wrap: break-word;
+ width: 100%;
+}
+
+.arrow {
+ position: absolute;
+ color: #4299e1;
+ font-weight: bold;
+ font-size: 18px;
+ right: 4px;
+ top: 50%;
+ transform: translateY(-50%);
+}
+
+.grid-cell.blocked-cell {
+ background: #2d3748;
+ border-color: #2d3748;
+ cursor: default;
+}
+
+.grid-cell.letter-cell {
+ background: white;
+ font-size: 28px;
+ color: #2d3748;
+ cursor: pointer;
+}
+
+.grid-cell.letter-cell:hover {
+ background: #f7fafc;
+ border-color: #4299e1;
+}
+
+.grid-cell.letter-cell.active {
+ background: #ebf8ff;
+ border-color: #3182ce;
+ box-shadow: 0 0 0 3px rgba(66, 153, 225, 0.4);
+}
+
+.grid-cell.letter-cell.highlighted {
+ background: #fef5e7;
+ border-color: #f39c12;
+}
+
+.grid-cell.letter-cell.correct {
+ background: #c6f6d5;
+ border-color: #48bb78;
+ animation: correctPulse 0.5s ease;
+}
+
+.grid-cell.letter-cell.incorrect {
+ background: #fed7d7;
+ border-color: #f56565;
+ animation: shake 0.4s ease;
+}
+
+@keyframes correctPulse {
+ 0%, 100% { transform: scale(1); }
+ 50% { transform: scale(1.1); }
+}
+
+@keyframes shake {
+ 0%, 100% { transform: translateX(0); }
+ 25% { transform: translateX(-5px); }
+ 75% { transform: translateX(5px); }
+}
+
+@keyframes wordComplete {
+ 0% { box-shadow: 0 0 0 0 rgba(72, 187, 120, 0.7); }
+ 100% { box-shadow: 0 0 0 20px rgba(72, 187, 120, 0); }
+}
+
+.word-complete {
+ animation: wordComplete 0.8s ease;
+}
+
+/* On-Screen Keyboard */
+.keyboard-area {
+ background: #2d3748;
+ padding: 12px 10px 10px 10px;
+ flex-shrink: 0;
+ box-shadow: 0 -2px 10px rgba(0,0,0,0.2);
+}
+
+.keyboard-row {
+ display: flex;
+ justify-content: center;
+ gap: 6px;
+ margin-bottom: 8px;
+}
+
+.key-btn {
+ background: linear-gradient(135deg, #4a5568, #2d3748);
+ border: none;
+ color: white;
+ min-width: 50px;
+ height: 52px;
+ border-radius: 8px;
+ font-size: 20px;
+ font-weight: 600;
+ cursor: pointer;
+ transition: all 0.15s;
+ box-shadow: 0 2px 5px rgba(0,0,0,0.2);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+
+.key-btn:active {
+ transform: scale(0.95);
+ background: linear-gradient(135deg, #667eea, #764ba2);
+}
+
+.key-btn.key-action {
+ background: linear-gradient(135deg, #ed8936, #dd6b20);
+ min-width: 60px;
+}
+
+.key-btn.key-del {
+ background: linear-gradient(135deg, #fc8181, #f56565);
+ min-width: 60px;
+}
+
+.keyboard-actions {
+ display: flex;
+ gap: 8px;
+ margin-top: 10px;
+ justify-content: center;
+}
+
+.action-btn {
+ background: linear-gradient(135deg, #48bb78, #38a169);
+ border: none;
+ color: white;
+ padding: 14px 28px;
+ border-radius: 10px;
+ font-size: 16px;
+ font-weight: 600;
+ cursor: pointer;
+ box-shadow: 0 3px 8px rgba(0,0,0,0.2);
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ transition: all 0.2s;
+}
+
+.action-btn:active {
+ transform: scale(0.97);
+}
+
+.action-btn.secondary {
+ background: linear-gradient(135deg, #667eea, #764ba2);
+}
+
+/* Side Menu */
+.side-menu {
+ position: fixed;
+ top: 0;
+ left: -300px;
+ width: 300px;
+ height: 100vh;
+ background: white;
+ box-shadow: 2px 0 15px rgba(0,0,0,0.3);
+ transition: left 0.3s ease;
+ z-index: 1000;
+}
+
+.side-menu.active {
+ left: 0;
+}
+
+.menu-header {
+ background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
+ color: white;
+ padding: 20px;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+}
+
+.menu-header h3 {
+ font-size: 20px;
+}
+
+.close-menu {
+ background: none;
+ border: none;
+ color: white;
+ font-size: 24px;
+ cursor: pointer;
+}
+
+.menu-items {
+ padding: 15px 0;
+}
+
+.menu-item {
+ display: flex;
+ align-items: center;
+ gap: 15px;
+ padding: 18px 20px;
+ border: none;
+ background: none;
+ width: 100%;
+ text-align: left;
+ cursor: pointer;
+ font-size: 16px;
+ transition: background 0.2s;
+}
+
+.menu-item:active {
+ background: #f8f9fa;
+}
+
+.menu-item i {
+ width: 24px;
+ color: #4facfe;
+ font-size: 20px;
+}
+
+/* Modal */
+.modal {
+ display: none;
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background: rgba(0,0,0,0.7);
+ z-index: 2000;
+ justify-content: center;
+ align-items: center;
+}
+
+.modal.active {
+ display: flex;
+}
+
+.modal-content {
+ background: white;
+ border-radius: 20px;
+ padding: 40px;
+ text-align: center;
+ max-width: 450px;
+ width: 90%;
+ box-shadow: 0 10px 40px rgba(0,0,0,0.4);
+}
+
+.success-icon {
+ font-size: 80px;
+ color: #48bb78;
+ margin-bottom: 20px;
+ animation: bounce 1s ease;
+}
+
+@keyframes bounce {
+ 0%, 20%, 50%, 80%, 100% { transform: translateY(0); }
+ 40% { transform: translateY(-20px); }
+ 60% { transform: translateY(-10px); }
+}
+
+.modal-content h2 {
+ color: #2d3748;
+ margin-bottom: 12px;
+ font-size: 28px;
+}
+
+.modal-content p {
+ color: #4a5568;
+ margin-bottom: 24px;
+ font-size: 18px;
+}
+
+.btn-primary {
+ background: linear-gradient(135deg, #48bb78, #38a169);
+ color: white;
+ border: none;
+ border-radius: 12px;
+ padding: 16px 32px;
+ cursor: pointer;
+ font-weight: 600;
+ font-size: 18px;
+ box-shadow: 0 4px 12px rgba(72, 187, 120, 0.4);
+}
+
+.btn-primary:active {
+ transform: scale(0.98);
+}
+
+/* Toast Notification */
+.toast {
+ position: fixed;
+ top: 80px;
+ left: 50%;
+ transform: translateX(-50%);
+ background: rgba(0,0,0,0.9);
+ color: white;
+ padding: 14px 28px;
+ border-radius: 25px;
+ font-size: 16px;
+ font-weight: 600;
+ z-index: 9999;
+ animation: toastFade 2.5s ease;
+ box-shadow: 0 4px 15px rgba(0,0,0,0.3);
+}
+
+@keyframes toastFade {
+ 0%, 100% { opacity: 0; transform: translateX(-50%) translateY(-10px); }
+ 15%, 85% { opacity: 1; transform: translateX(-50%) translateY(0); }
+}
diff --git a/public/tablet-app.html b/public/tablet-app.html
new file mode 100644
index 0000000..49e9cb1
--- /dev/null
+++ b/public/tablet-app.html
@@ -0,0 +1,138 @@
+
+
+
+
+
+
+
+
+ Kruiswoord Pro
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Gefeliciteerd!
+
+50 Munten | +1 Hint
+
+
+
+
+
+
+
+
+
diff --git a/public/tablet-app.js b/public/tablet-app.js
new file mode 100644
index 0000000..e55a441
--- /dev/null
+++ b/public/tablet-app.js
@@ -0,0 +1,657 @@
+// Full-Featured Tablet Crossword App
+class TabletCrosswordApp {
+ constructor() {
+ this.currentLevel = 1
+ this.coins = 100
+ this.hints = 3
+ this.currentPuzzle = null
+ this.userAnswers = {}
+ this.selectedCell = null
+ this.selectedWord = null
+ this.gridElement = document.getElementById('crosswordGrid')
+ this.cells = []
+
+ this.init()
+ }
+
+ init() {
+ this.loadGameState()
+ this.loadPuzzle(this.currentLevel)
+ this.setupEventListeners()
+ this.updateUI()
+ this.enableFullscreen()
+ }
+
+ enableFullscreen() {
+ // Request fullscreen on first interaction
+ document.addEventListener('click', () => {
+ if (document.documentElement.requestFullscreen && !document.fullscreenElement) {
+ document.documentElement.requestFullscreen().catch(() => {})
+ }
+ }, { once: true })
+ }
+
+ setupEventListeners() {
+ // Menu
+ document.getElementById('menuBtn').addEventListener('click', () => {
+ document.getElementById('sideMenu').classList.add('active')
+ })
+ document.getElementById('closeMenu').addEventListener('click', () => {
+ document.getElementById('sideMenu').classList.remove('active')
+ })
+
+ // On-screen keyboard
+ document.querySelectorAll('.key-btn').forEach(btn => {
+ if (!btn.id) {
+ btn.addEventListener('click', () => {
+ const letter = btn.textContent.trim()
+ this.handleLetterInput(letter)
+ })
+ }
+ })
+
+ document.getElementById('delKey').addEventListener('click', () => this.handleDelete())
+ document.getElementById('hintKey').addEventListener('click', () => this.useHintForCurrentWord())
+
+ // Actions
+ document.getElementById('checkBtn').addEventListener('click', () => this.checkAllAnswers())
+ document.getElementById('clearBtn').addEventListener('click', () => this.clearGrid())
+
+ // Menu items
+ document.getElementById('newGame').addEventListener('click', () => this.newGame())
+ document.getElementById('dailyPuzzle').addEventListener('click', () => {
+ this.showToast('Dagelijkse puzzel komt binnenkort!')
+ document.getElementById('sideMenu').classList.remove('active')
+ })
+ document.getElementById('levelSelect').addEventListener('click', () => {
+ this.showToast('Niveau selectie komt binnenkort!')
+ document.getElementById('sideMenu').classList.remove('active')
+ })
+
+ document.getElementById('nextBtn').addEventListener('click', () => this.nextPuzzle())
+
+ // Prevent accidental zoom/scroll
+ document.addEventListener('gesturestart', e => e.preventDefault())
+ document.addEventListener('gesturechange', e => e.preventDefault())
+ }
+
+ loadPuzzle(level) {
+ if (typeof DUTCH_PUZZLES !== 'undefined') {
+ this.currentPuzzle = DUTCH_PUZZLES[`level${level}`] || DUTCH_PUZZLES.level1
+ }
+
+ this.renderGrid()
+ this.updateProgress()
+ this.updateUI()
+ }
+
+ renderGrid() {
+ if (!this.currentPuzzle) return
+
+ const grid = this.currentPuzzle.grid
+ const words = this.currentPuzzle.words
+
+ this.gridElement.innerHTML = ''
+ this.cells = []
+
+ const rows = grid.length
+ const cols = Math.max(...grid.map(row => row.length)) + 1
+ const cellSize = this.calculateCellSize(rows, cols)
+
+ this.gridElement.style.gridTemplateColumns = `repeat(${cols}, ${cellSize}px)`
+ this.gridElement.style.gridTemplateRows = `repeat(${rows}, ${cellSize}px)`
+
+ // Create cells
+ for (let row = 0; row < rows; row++) {
+ for (let col = 0; col < cols; col++) {
+ if (col === 0) {
+ const clueData = this.getClueForRow(row, words)
+ this.gridElement.appendChild(this.createClueCell(clueData, cellSize))
+ } else {
+ const gridCol = col - 1
+ const cellValue = grid[row]?.[gridCol] || '#'
+ const cell = this.createCell(cellValue, row, gridCol, words, cellSize)
+ this.gridElement.appendChild(cell)
+
+ if (cell.classList.contains('letter-cell')) {
+ this.cells.push({ element: cell, row, col: gridCol })
+ }
+ }
+ }
+ }
+ }
+
+ calculateCellSize(rows, cols) {
+ const headerHeight = 66
+ const progressHeight = 40
+ const keyboardHeight = 230
+ const padding = 30
+
+ const availableHeight = window.innerHeight - headerHeight - progressHeight - keyboardHeight - padding
+ const availableWidth = window.innerWidth - padding
+
+ const maxFromHeight = Math.floor(availableHeight / rows)
+ const maxFromWidth = Math.floor(availableWidth / cols)
+
+ let cellSize = Math.min(maxFromHeight, maxFromWidth)
+ cellSize = Math.max(50, Math.min(75, cellSize))
+
+ return cellSize
+ }
+
+ getClueForRow(row, words) {
+ return words.find(w => w.direction === 'horizontal' && w.startRow === row)
+ }
+
+ createClueCell(clueData, cellSize) {
+ const cell = document.createElement('div')
+ cell.className = 'grid-cell clue-cell'
+ cell.style.width = `${cellSize}px`
+ cell.style.height = `${cellSize}px`
+
+ if (clueData) {
+ const text = document.createElement('div')
+ text.className = 'clue-text'
+ text.textContent = clueData.clue
+
+ const arrow = document.createElement('div')
+ arrow.className = 'arrow'
+ arrow.textContent = '→'
+
+ cell.appendChild(text)
+ cell.appendChild(arrow)
+ } else {
+ cell.classList.add('blocked-cell')
+ }
+
+ return cell
+ }
+
+ createCell(value, row, col, words, cellSize) {
+ const cell = document.createElement('div')
+ cell.className = 'grid-cell'
+ cell.style.width = `${cellSize}px`
+ cell.style.height = `${cellSize}px`
+ cell.dataset.row = row
+ cell.dataset.col = col
+
+ if (value === '#') {
+ const clueData = this.getClueAtPosition(row, col, words)
+ if (clueData) {
+ cell.classList.add('clue-cell')
+ const text = document.createElement('div')
+ text.className = 'clue-text'
+ text.textContent = clueData.clue
+ const arrow = document.createElement('div')
+ arrow.className = 'arrow'
+ arrow.textContent = '→'
+ cell.appendChild(text)
+ cell.appendChild(arrow)
+ } else {
+ cell.classList.add('blocked-cell')
+ }
+ } else {
+ cell.classList.add('letter-cell')
+ const key = `${row}-${col}`
+ cell.textContent = this.userAnswers[key] || ''
+
+ // Click to select and highlight word
+ cell.addEventListener('click', () => {
+ this.selectCell(cell, row, col)
+ this.highlightWord(row, col)
+ })
+ }
+
+ return cell
+ }
+
+ getClueAtPosition(row, col, words) {
+ for (const word of words) {
+ if (word.direction === 'horizontal' && word.startRow === row && word.startCol - 1 === col) {
+ return word
+ }
+ }
+ return null
+ }
+
+ selectCell(cell, row, col) {
+ this.cells.forEach(c => c.element.classList.remove('active'))
+ cell.classList.add('active')
+ this.selectedCell = { element: cell, row, col }
+ }
+
+ highlightWord(row, col) {
+ // Clear previous highlights
+ this.cells.forEach(c => c.element.classList.remove('highlighted'))
+
+ // Find word containing this cell
+ const word = this.findWordContainingCell(row, col)
+ if (!word) return
+
+ this.selectedWord = word
+
+ // Highlight all cells in this word
+ if (word.direction === 'horizontal') {
+ for (let c = word.startCol; c < word.startCol + word.answer.length; c++) {
+ const cellData = this.cells.find(cell => cell.row === word.startRow && cell.col === c)
+ if (cellData) {
+ cellData.element.classList.add('highlighted')
+ }
+ }
+ } else {
+ for (let r = word.startRow; r < word.startRow + word.answer.length; r++) {
+ const cellData = this.cells.find(cell => cell.row === r && cell.col === word.startCol)
+ if (cellData) {
+ cellData.element.classList.add('highlighted')
+ }
+ }
+ }
+ }
+
+ findWordContainingCell(row, col) {
+ for (const word of this.currentPuzzle.words) {
+ if (word.direction === 'horizontal') {
+ if (word.startRow === row && col >= word.startCol && col < word.startCol + word.answer.length) {
+ return word
+ }
+ } else if (word.direction === 'vertical') {
+ if (word.startCol === col && row >= word.startRow && row < word.startRow + word.answer.length) {
+ return word
+ }
+ }
+ }
+ return null
+ }
+
+ handleLetterInput(letter) {
+ if (!this.selectedCell) {
+ // Auto-select first empty cell
+ const firstEmpty = this.cells.find(c => !c.element.textContent)
+ if (firstEmpty) {
+ this.selectCell(firstEmpty.element, firstEmpty.row, firstEmpty.col)
+ this.highlightWord(firstEmpty.row, firstEmpty.col)
+ } else {
+ return
+ }
+ }
+
+ const { element, row, col } = this.selectedCell
+ element.textContent = letter
+ const key = `${row}-${col}`
+ this.userAnswers[key] = letter
+
+ this.saveGameState()
+ this.updateProgress()
+
+ // Check if word is complete
+ this.checkWordComplete()
+
+ // Move to next cell in word
+ this.moveToNextInWord()
+ }
+
+ handleDelete() {
+ if (!this.selectedCell) return
+
+ const { element, row, col } = this.selectedCell
+
+ if (element.textContent) {
+ // Delete current cell
+ element.textContent = ''
+ const key = `${row}-${col}`
+ delete this.userAnswers[key]
+ element.classList.remove('correct', 'incorrect')
+ } else {
+ // Move to previous cell and delete
+ this.moveToPreviousInWord()
+ if (this.selectedCell) {
+ const { element: prevElement, row: prevRow, col: prevCol } = this.selectedCell
+ prevElement.textContent = ''
+ const key = `${prevRow}-${prevCol}`
+ delete this.userAnswers[key]
+ prevElement.classList.remove('correct', 'incorrect')
+ }
+ }
+
+ this.saveGameState()
+ this.updateProgress()
+ }
+
+ moveToNextInWord() {
+ if (!this.selectedCell || !this.selectedWord) return
+
+ const { row, col } = this.selectedCell
+ const word = this.selectedWord
+
+ let nextRow = row
+ let nextCol = col
+
+ if (word.direction === 'horizontal') {
+ nextCol++
+ if (nextCol >= word.startCol + word.answer.length) return
+ } else {
+ nextRow++
+ if (nextRow >= word.startRow + word.answer.length) return
+ }
+
+ const nextCell = this.cells.find(c => c.row === nextRow && c.col === nextCol)
+ if (nextCell) {
+ this.selectCell(nextCell.element, nextCell.row, nextCell.col)
+ }
+ }
+
+ moveToPreviousInWord() {
+ if (!this.selectedCell || !this.selectedWord) return
+
+ const { row, col } = this.selectedCell
+ const word = this.selectedWord
+
+ let prevRow = row
+ let prevCol = col
+
+ if (word.direction === 'horizontal') {
+ prevCol--
+ if (prevCol < word.startCol) return
+ } else {
+ prevRow--
+ if (prevRow < word.startRow) return
+ }
+
+ const prevCell = this.cells.find(c => c.row === prevRow && c.col === prevCol)
+ if (prevCell) {
+ this.selectCell(prevCell.element, prevCell.row, prevCell.col)
+ }
+ }
+
+ checkWordComplete() {
+ if (!this.selectedWord) return
+
+ const word = this.selectedWord
+ let userWord = ''
+ let allFilled = true
+
+ if (word.direction === 'horizontal') {
+ for (let c = word.startCol; c < word.startCol + word.answer.length; c++) {
+ const key = `${word.startRow}-${c}`
+ const letter = this.userAnswers[key]
+ if (!letter) {
+ allFilled = false
+ break
+ }
+ userWord += letter
+ }
+ } else {
+ for (let r = word.startRow; r < word.startRow + word.answer.length; r++) {
+ const key = `${r}-${word.startCol}`
+ const letter = this.userAnswers[key]
+ if (!letter) {
+ allFilled = false
+ break
+ }
+ userWord += letter
+ }
+ }
+
+ if (allFilled) {
+ // Auto-validate word
+ if (userWord === word.answer) {
+ this.animateWordSuccess(word)
+ } else {
+ this.animateWordError(word)
+ }
+ }
+ }
+
+ animateWordSuccess(word) {
+ const cells = []
+
+ if (word.direction === 'horizontal') {
+ for (let c = word.startCol; c < word.startCol + word.answer.length; c++) {
+ const cellData = this.cells.find(cell => cell.row === word.startRow && cell.col === c)
+ if (cellData) cells.push(cellData.element)
+ }
+ } else {
+ for (let r = word.startRow; r < word.startRow + word.answer.length; r++) {
+ const cellData = this.cells.find(cell => cell.row === r && cell.col === word.startCol)
+ if (cellData) cells.push(cellData.element)
+ }
+ }
+
+ cells.forEach((cell, index) => {
+ setTimeout(() => {
+ cell.classList.remove('incorrect', 'highlighted')
+ cell.classList.add('correct', 'word-complete')
+
+ setTimeout(() => {
+ cell.classList.remove('word-complete')
+ }, 800)
+ }, index * 100)
+ })
+
+ this.showToast('Correct! 🎉')
+ this.checkPuzzleComplete()
+ }
+
+ animateWordError(word) {
+ const cells = []
+
+ if (word.direction === 'horizontal') {
+ for (let c = word.startCol; c < word.startCol + word.answer.length; c++) {
+ const cellData = this.cells.find(cell => cell.row === word.startRow && cell.col === c)
+ if (cellData) cells.push(cellData.element)
+ }
+ } else {
+ for (let r = word.startRow; r < word.startRow + word.answer.length; r++) {
+ const cellData = this.cells.find(cell => cell.row === r && cell.col === word.startCol)
+ if (cellData) cells.push(cellData.element)
+ }
+ }
+
+ cells.forEach(cell => {
+ cell.classList.remove('correct')
+ cell.classList.add('incorrect')
+
+ setTimeout(() => {
+ cell.classList.remove('incorrect')
+ }, 1000)
+ })
+
+ this.showToast('Probeer opnieuw')
+ }
+
+ checkPuzzleComplete() {
+ const allCorrect = this.cells.every(cell => {
+ const row = cell.row
+ const col = cell.col
+ const key = `${row}-${col}`
+ const userAnswer = this.userAnswers[key]
+ const correctAnswer = this.getCorrectAnswer(row, col)
+ return userAnswer === correctAnswer
+ })
+
+ if (allCorrect) {
+ setTimeout(() => this.showSuccess(), 500)
+ }
+ }
+
+ checkAllAnswers() {
+ this.cells.forEach(cell => {
+ const row = cell.row
+ const col = cell.col
+ const key = `${row}-${col}`
+ const userAnswer = this.userAnswers[key]
+ const correctAnswer = this.getCorrectAnswer(row, col)
+
+ if (userAnswer === correctAnswer) {
+ cell.element.classList.add('correct')
+ cell.element.classList.remove('incorrect')
+ } else if (userAnswer) {
+ cell.element.classList.add('incorrect')
+ cell.element.classList.remove('correct')
+ }
+ })
+
+ this.checkPuzzleComplete()
+ }
+
+ getCorrectAnswer(row, col) {
+ for (const word of this.currentPuzzle.words) {
+ if (word.direction === 'horizontal') {
+ if (word.startRow === row && col >= word.startCol && col < word.startCol + word.answer.length) {
+ return word.answer[col - word.startCol]
+ }
+ } else if (word.direction === 'vertical') {
+ if (word.startCol === col && row >= word.startRow && row < word.startRow + word.answer.length) {
+ return word.answer[row - word.startRow]
+ }
+ }
+ }
+ return null
+ }
+
+ useHintForCurrentWord() {
+ if (this.hints <= 0) {
+ this.showToast('Geen hints meer!')
+ return
+ }
+
+ if (!this.selectedWord) {
+ this.showToast('Selecteer eerst een woord')
+ return
+ }
+
+ const word = this.selectedWord
+ const emptyCells = []
+
+ if (word.direction === 'horizontal') {
+ for (let c = word.startCol; c < word.startCol + word.answer.length; c++) {
+ const key = `${word.startRow}-${c}`
+ if (!this.userAnswers[key]) {
+ emptyCells.push({ row: word.startRow, col: c, letter: word.answer[c - word.startCol] })
+ }
+ }
+ } else {
+ for (let r = word.startRow; r < word.startRow + word.answer.length; r++) {
+ const key = `${r}-${word.startCol}`
+ if (!this.userAnswers[key]) {
+ emptyCells.push({ row: r, col: word.startCol, letter: word.answer[r - word.startRow] })
+ }
+ }
+ }
+
+ if (emptyCells.length === 0) {
+ this.showToast('Woord al compleet!')
+ return
+ }
+
+ const randomCell = emptyCells[Math.floor(Math.random() * emptyCells.length)]
+ const key = `${randomCell.row}-${randomCell.col}`
+ this.userAnswers[key] = randomCell.letter
+
+ const cellData = this.cells.find(c => c.row === randomCell.row && c.col === randomCell.col)
+ if (cellData) {
+ cellData.element.textContent = randomCell.letter
+ }
+
+ this.hints--
+ this.updateUI()
+ this.saveGameState()
+ this.showToast('Hint gebruikt!')
+ this.checkWordComplete()
+ }
+
+ clearGrid() {
+ if (confirm('Alles wissen?')) {
+ this.userAnswers = {}
+ this.cells.forEach(cell => {
+ cell.element.textContent = ''
+ cell.element.classList.remove('correct', 'incorrect', 'highlighted')
+ })
+ this.saveGameState()
+ this.updateProgress()
+ this.showToast('Gewist')
+ }
+ }
+
+ newGame() {
+ if (confirm('Nieuw spel starten?')) {
+ this.currentLevel = 1
+ this.userAnswers = {}
+ this.loadPuzzle(1)
+ this.saveGameState()
+ document.getElementById('sideMenu').classList.remove('active')
+ this.showToast('Nieuw spel gestart!')
+ }
+ }
+
+ showSuccess() {
+ this.coins += 50
+ this.hints += 1
+ this.updateUI()
+ this.saveGameState()
+ document.getElementById('successModal').classList.add('active')
+ }
+
+ nextPuzzle() {
+ document.getElementById('successModal').classList.remove('active')
+ this.currentLevel++
+ this.userAnswers = {}
+ this.loadPuzzle(this.currentLevel)
+ this.saveGameState()
+ this.showToast(`Niveau ${this.currentLevel}`)
+ }
+
+ updateProgress() {
+ const total = this.cells.length
+ const filled = Object.keys(this.userAnswers).length
+ const progress = total > 0 ? Math.round((filled / total) * 100) : 0
+
+ document.getElementById('progressFill').style.width = `${progress}%`
+ document.getElementById('progressText').textContent = `${progress}%`
+ }
+
+ updateUI() {
+ document.getElementById('coinsCount').textContent = this.coins
+ document.getElementById('hintsCount').textContent = this.hints
+ document.getElementById('levelNum').textContent = this.currentLevel
+ }
+
+ showToast(message) {
+ const toast = document.createElement('div')
+ toast.className = 'toast'
+ toast.textContent = message
+ document.body.appendChild(toast)
+
+ setTimeout(() => {
+ toast.remove()
+ }, 2500)
+ }
+
+ // LocalStorage - Game State Persistence
+ saveGameState() {
+ const state = {
+ currentLevel: this.currentLevel,
+ coins: this.coins,
+ hints: this.hints,
+ userAnswers: this.userAnswers,
+ timestamp: Date.now()
+ }
+ localStorage.setItem('crosswordGameState', JSON.stringify(state))
+ }
+
+ loadGameState() {
+ const saved = localStorage.getItem('crosswordGameState')
+ if (saved) {
+ const state = JSON.parse(saved)
+ this.currentLevel = state.currentLevel || 1
+ this.coins = state.coins || 100
+ this.hints = state.hints || 3
+ this.userAnswers = state.userAnswers || {}
+ }
+ }
+}
+
+// Initialize
+document.addEventListener('DOMContentLoaded', () => {
+ new TabletCrosswordApp()
+})
diff --git a/public/tablet.html b/public/tablet.html
index 203543a..90668f6 100644
--- a/public/tablet.html
+++ b/public/tablet.html
@@ -4,7 +4,7 @@
Kruiswoord Pro
-
+