{"id":107,"date":"2026-01-02T12:54:06","date_gmt":"2026-01-02T12:54:06","guid":{"rendered":"https:\/\/jaivey.brandburgservice.nl\/?page_id=107"},"modified":"2026-01-02T12:57:53","modified_gmt":"2026-01-02T12:57:53","slug":"memory-transport_2","status":"publish","type":"page","link":"https:\/\/jaivey.brandburgservice.nl\/?page_id=107","title":{"rendered":"Memory transport_2"},"content":{"rendered":"\n<!doctype html>\n<html lang=\"nl\">\n<head>\n<meta charset=\"utf-8\" \/>\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\" \/>\n<title>Memory &#8211; Transport (eigen, reclamevrij)<\/title>\n<style>\n  :root{\n    --bg:#f3f6fb;\n    --card:#ffffff;\n    --accent:#0f6ef1;\n    --muted:#6b7280;\n    --success:#10b981;\n    --shadow: 0 6px 18px rgba(16,24,40,0.08);\n    --glass: rgba(255,255,255,0.6);\n  }\n  *, *::before, *::after { box-sizing: border-box; }\n  html,body{height:100%;margin:0;}\n  body{\n    font-family:Inter,ui-sans-serif,system-ui,-apple-system,\"Segoe UI\",Roboto,\"Helvetica Neue\",Arial;\n    background:linear-gradient(180deg,var(--bg),#eaf1ff);\n    display:flex;\n    align-items:flex-start;\n    justify-content:center;\n    padding:12px;\n    color:#0f172a;\n  }\n  .container{\n    width:100%;\n    max-width:480px;\n    background:var(--glass);\n    border-radius:16px;\n    padding:16px;\n    box-shadow:var(--shadow);\n    backdrop-filter: blur(6px) saturate(120%);\n  }\n  header{\n    display:flex;\n    gap:12px;\n    align-items:center;\n    justify-content:space-between;\n    margin-bottom:14px;\n    flex-wrap:wrap;\n  }\n  h1{font-size:18px;margin:0;}\n  .controls{\n    display:flex;\n    gap:8px;\n    flex-wrap:wrap;\n    align-items:center;\n  }\n  .stat{\n    background:var(--card);\n    padding:6px 8px;\n    border-radius:10px;\n    box-shadow: 0 4px 12px rgba(2,6,23,0.06);\n    font-size:12px;\n  }\n  button.btn{\n    border:0;\n    padding:6px 10px;\n    border-radius:10px;\n    background:var(--accent);\n    color:#fff;\n    font-weight:600;\n    cursor:pointer;\n    font-size:14px;\n    box-shadow: 0 6px 10px rgba(15,110,241,0.18);\n  }\n  button.btn.secondary{\n    background:transparent;\n    color:var(--accent);\n    border:1px solid rgba(15,110,241,0.12);\n    box-shadow:none;\n  }\n  .topline{display:flex;gap:8px;align-items:center;flex-wrap:wrap;margin-bottom:10px;}\n  .grid{\n    display:grid;\n    grid-template-columns: repeat(6, 1fr);\n    gap:8px;\n    margin-top:18px;\n  }\n  .card{\n    position:relative;\n    width:100%;\n    padding-top:100%;\n    border-radius:12px;\n    perspective:1000px;\n  }\n  .card-inner{\n    position:absolute;\n    inset:0;\n    border-radius:12px;\n    transform-style:preserve-3d;\n    transition: transform 380ms cubic-bezier(.2,.9,.3,1);\n  }\n  .card.flipped .card-inner{ transform: rotateY(180deg); }\n  .face{\n    position:absolute;\n    inset:0;\n    display:flex;\n    align-items:center;\n    justify-content:center;\n    backface-visibility:hidden;\n    border-radius:12px;\n    box-shadow: 0 6px 14px rgba(2,6,23,0.06);\n    font-size:32px;\n  }\n  .face.back{\n    background:var(--card);\n    transform: rotateY(180deg);\n  }\n  .face.front{\n    background: linear-gradient(180deg, #0f6ef1 0%, #3aa0ff 100%);\n    color:white;\n    font-weight:700;\n    font-size:28px;\n    display:flex;\n    flex-direction:column;\n    gap:6px;\n    padding:12px;\n  }\n  .badge{font-size:12px;opacity:0.92}\n  .settings{display:flex;gap:8px;align-items:center;flex-wrap:wrap;}\n  select,input[type=\"range\"]{padding:6px;border-radius:8px;border:1px solid rgba(2,6,23,0.06);background:white;font-size:12px;}\n  .message{margin-top:12px;padding:12px;background:#fff;border-radius:10px;box-shadow:var(--shadow);display:none;font-size:13px;}\n  .message.show{display:block}\n  .footer{margin-top:16px;display:flex;justify-content:space-between;align-items:center;font-size:12px;color:var(--muted);flex-wrap:wrap;gap:4px;}\n  .hidden{display:none}\n\n  \/* Responsive grid en fonts *\/\n  @media (max-width:900px){ .grid{grid-template-columns: repeat(5,1fr);} }\n  @media (max-width:700px){ \n    .grid{grid-template-columns: repeat(4,1fr);} \n    header{flex-direction:column;align-items:flex-start} \n  }\n  @media (max-width:400px){\n    .grid{grid-template-columns: repeat(3,1fr);gap:4px;}\n    .card .face{font-size:24px !important;}\n    button.btn{padding:5px 8px;font-size:12px;}\n    .stat{font-size:11px;padding:5px 6px;}\n    h1{font-size:16px;}\n  }\n<\/style>\n<\/head>\n<body>\n<div class=\"container\" role=\"application\" aria-label=\"Memory spel\">\n  <header>\n    <div>\n      <h1>Memory \u2014 Transport (eigen, reclamevrij)<\/h1>\n      <div style=\"color:var(--muted);font-size:12px;margin-top:4px\">\n        Klik twee kaarten om een paar te zoeken. Gebruik je eigen afbeeldingen (zie code comments).\n      <\/div>\n    <\/div>\n\n    <div class=\"controls\">\n      <div class=\"stat\" id=\"moves\">Bewegingen: 0<\/div>\n      <div class=\"stat\" id=\"pairs\">Gevonden paren: 0<\/div>\n      <div class=\"stat\" id=\"timer\">Tijd: 00:00<\/div>\n      <div style=\"display:flex;gap:6px;flex-wrap:wrap\">\n        <button class=\"btn\" id=\"newBtn\">Nieuw spel<\/button>\n        <button class=\"btn secondary\" id=\"hintBtn\">Hint<\/button>\n      <\/div>\n    <\/div>\n  <\/header>\n\n  <div class=\"topline\">\n    <div class=\"settings\">\n      <label>Paren:\n        <select id=\"pairCount\">\n          <option value=\"6\">6 paren (klein)<\/option>\n          <option value=\"8\" selected>8 paren (medium)<\/option>\n          <option value=\"10\">10 paren (groot)<\/option>\n          <option value=\"12\">12 paren (zeer groot)<\/option>\n        <\/select>\n      <\/label>\n      <label>Timer:\n        <select id=\"timeMode\">\n          <option value=\"free\" selected>Vrij<\/option>\n          <option value=\"60\">60s uitdaging<\/option>\n          <option value=\"90\">90s uitdaging<\/option>\n        <\/select>\n      <\/label>\n    <\/div>\n  <\/div>\n\n  <main id=\"board\" class=\"grid\" aria-live=\"polite\"><\/main>\n\n  <div class=\"message\" id=\"message\" role=\"status\"><\/div>\n\n  <div class=\"footer\">\n    <div>Deze versie is advertentievrij en volledig lokaal \u2014 gebruik je eigen afbeeldingen door `cards` array aan te passen.<\/div>\n    <div style=\"color:var(--muted)\">\u00a9 Eigen site<\/div>\n  <\/div>\n<\/div>\n\n<script>\nconst defaultCards = [\n  { id:'truck', label:'\ud83d\ude9a' },\n  { id:'bus', label:'\ud83d\ude8c' },\n  { id:'scooter', label:'\ud83d\udef5' },\n  { id:'car', label:'\ud83d\ude97' },\n  { id:'train', label:'\ud83d\ude86' },\n  { id:'ship', label:'\ud83d\udea2' },\n  { id:'plane', label:'\u2708\ufe0f' },\n  { id:'bicycle', label:'\ud83d\udeb2' },\n  { id:'tram', label:'\ud83d\ude8b' },\n  { id:'helicopter', label:'\ud83d\ude81' },\n  { id:'rocket', label:'\ud83d\ude80' },\n  { id:'Jaivey', label:'\ufffd Jaivey' }\n];\n\nconst boardEl = document.getElementById('board');\nconst movesEl = document.getElementById('moves');\nconst pairsEl = document.getElementById('pairs');\nconst timerEl = document.getElementById('timer');\nconst messageEl = document.getElementById('message');\nconst newBtn = document.getElementById('newBtn');\nconst hintBtn = document.getElementById('hintBtn');\nconst pairCountSelect = document.getElementById('pairCount');\nconst timeModeSelect = document.getElementById('timeMode');\n\nlet pairCount = parseInt(pairCountSelect.value,10);\nlet timeMode = timeModeSelect.value;\nlet deck = [];\nlet firstCard = null;\nlet secondCard = null;\nlet lockBoard = false;\nlet moves = 0;\nlet foundPairs = 0;\nlet timerInterval = null;\nlet timeLeft = 0;\nlet elapsed = 0;\n\nfunction formatTime(s){\n  const mm = String(Math.floor(s\/60)).padStart(2,'0');\n  const ss = String(s%60).padStart(2,'0');\n  return mm + ':' + ss;\n}\n\nfunction shuffle(a){\n  for(let i=a.length-1;i>0;i--){\n    const j = Math.floor(Math.random()*(i+1));\n    [a[i],a[j]] = [a[j],a[i]];\n  }\n  return a;\n}\n\nfunction buildDeck(){\n  const source = defaultCards.slice();\n  shuffle(source);\n  const chosen = source.slice(0,pairCount);\n  let result = [];\n  chosen.forEach(item => {\n    result.push({...item});\n    result.push({...item});\n  });\n  shuffle(result);\n  return result;\n}\n\nfunction renderBoard(){\n  boardEl.innerHTML='';\n  deck.forEach((cardData, idx) => {\n    const card = document.createElement('div');\n    card.className='card';\n    card.dataset.index = idx;\n    card.setAttribute('role','button');\n    card.setAttribute('aria-label','Kaart');\n    card.tabIndex = 0;\n\n    const inner = document.createElement('div');\n    inner.className='card-inner';\n\n    const front = document.createElement('div');\n    front.className='face front';\n    front.innerHTML = `<div style=\"font-size:20px\">?<\/div><div class=\"badge\">Klik<\/div>`;\n\n    const back = document.createElement('div');\n    back.className='face back';\n    if(cardData.img){\n      const img = document.createElement('img');\n      img.src = cardData.img;\n      img.alt = cardData.label || '';\n      img.style.maxWidth='70%';\n      img.style.maxHeight='70%';\n      back.appendChild(img);\n    } else {\n      const lbl = document.createElement('div');\n      lbl.style.fontSize='32px';\n      lbl.textContent = cardData.label || '\ud83e\udde9';\n      back.appendChild(lbl);\n    }\n\n    inner.appendChild(front);\n    inner.appendChild(back);\n    card.appendChild(inner);\n\n    card.addEventListener('click', onCardClick);\n    card.addEventListener('keydown', (e) => {\n      if(e.key === 'Enter' || e.key === ' ') { e.preventDefault(); onCardClick.call(card,e); }\n    });\n\n    boardEl.appendChild(card);\n  });\n}\n\nfunction onCardClick(e){\n  if(lockBoard) return;\n  const cardEl = this;\n  const idx = parseInt(cardEl.dataset.index,10);\n  if(cardEl.classList.contains('matched') || cardEl.classList.contains('flipped')) return;\n\n  flipCard(cardEl);\n\n  if(!firstCard){\n    firstCard = { el: cardEl, idx };\n    return;\n  }\n\n  secondCard = { el: cardEl, idx };\n  moves++;\n  movesEl.textContent = `Bewegingen: ${moves}`;\n\n  checkMatch();\n}\n\nfunction flipCard(cardEl){ cardEl.classList.add('flipped'); }\nfunction unflip(cardEl){ cardEl.classList.remove('flipped'); }\n\nfunction checkMatch(){\n  const a = deck[firstCard.idx];\n  const b = deck[secondCard.idx];\n  if(a.id === b.id){\n    firstCard.el.classList.add('matched');\n    secondCard.el.classList.add('matched');\n    foundPairs++;\n    pairsEl.textContent = `Gevonden paren: ${foundPairs}`;\n    resetTurn(true);\n    if(foundPairs === pairCount){ win(); }\n  } else {\n    lockBoard = true;\n    setTimeout(()=> {\n      unflip(firstCard.el);\n      unflip(secondCard.el);\n      resetTurn(false);\n    }, 900);\n  }\n}\n\nfunction resetTurn(matched){ firstCard = null; secondCard = null; lockBoard = false; }\n\nfunction startTimer(){\n  clearInterval(timerInterval);\n  elapsed = 0;\n  if(timeMode === 'free'){\n    timerEl.textContent = 'Tijd: 00:00';\n    timerInterval = setInterval(()=> {\n      elapsed++;\n      timerEl.textContent = 'Tijd: ' + formatTime(elapsed);\n    },1000);\n  } else {\n    timeLeft = parseInt(timeMode,10);\n    timerEl.textContent = 'Tijd: ' + formatTime(timeLeft);\n    timerInterval = setInterval(()=> {\n      timeLeft--;\n      timerEl.textContent = 'Tijd: ' + formatTime(timeLeft);\n      if(timeLeft <= 0){\n        clearInterval(timerInterval);\n        lose();\n      }\n    },1000);\n  }\n}\n\nfunction stopTimer(){ clearInterval(timerInterval); }\n\nfunction win(){\n  stopTimer();\n  messageEl.classList.add('show');\n  messageEl.textContent = `Gewonnen! Je vond alle ${pairCount} paren in ${moves} bewegingen. ${ timeMode==='free' ? 'Tijd: '+formatTime(elapsed) : '' }`;\n}\n\nfunction lose(){\n  lockBoard = true;\n  messageEl.classList.add('show');\n  messageEl.textContent = `Tijd voorbij \u2014 helaas! Je hebt niet alle paren gevonden binnen de tijd.`;\n}\n\nfunction newGame(){\n  pairCount = parseInt(pairCountSelect.value,10);\n  timeMode = timeModeSelect.value;\n  deck = buildDeck();\n  moves = 0;\n  foundPairs = 0;\n  firstCard = secondCard = null;\n  lockBoard = false;\n  movesEl.textContent = `Bewegingen: ${moves}`;\n  pairsEl.textContent = `Gevonden paren: ${foundPairs}`;\n  messageEl.classList.remove('show');\n  renderBoard();\n  startTimer();\n}\n\nnewBtn.addEventListener('click', newGame);\npairCountSelect.addEventListener('change', () => newGame());\ntimeModeSelect.addEventListener('change', () => newGame());\nhintBtn.addEventListener('click', () => {\n  if(lockBoard) return;\n  const unmatched = Array.from(boardEl.querySelectorAll('.card:not(.matched):not(.flipped)'));\n  if(unmatched.length < 2) return;\n  shuffle(unmatched);\n  const a = unmatched[0];\n  const b = unmatched[1];\n  flipCard(a); flipCard(b);\n  lockBoard = true;\n  setTimeout(()=> { unflip(a); unflip(b); lockBoard = false; }, 1000);\n});\n\nnewGame();\n<\/script>\n<\/body>\n<\/html>\n\n\n","protected":false},"excerpt":{"rendered":"<p>Memory &#8211; Transport (eigen, reclamevrij) Memory \u2014 Transport (eigen, reclamevrij) Klik twee kaarten om een paar te zoeken. Gebruik je &hellip; <a href=\"https:\/\/jaivey.brandburgservice.nl\/?page_id=107\" class=\"more-link\"><span class=\"more-button\">Lees verder<span class=\"screen-reader-text\">Memory transport_2<\/span><\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"footnotes":""},"class_list":["post-107","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/jaivey.brandburgservice.nl\/index.php?rest_route=\/wp\/v2\/pages\/107","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/jaivey.brandburgservice.nl\/index.php?rest_route=\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/jaivey.brandburgservice.nl\/index.php?rest_route=\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/jaivey.brandburgservice.nl\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/jaivey.brandburgservice.nl\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=107"}],"version-history":[{"count":3,"href":"https:\/\/jaivey.brandburgservice.nl\/index.php?rest_route=\/wp\/v2\/pages\/107\/revisions"}],"predecessor-version":[{"id":112,"href":"https:\/\/jaivey.brandburgservice.nl\/index.php?rest_route=\/wp\/v2\/pages\/107\/revisions\/112"}],"wp:attachment":[{"href":"https:\/\/jaivey.brandburgservice.nl\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=107"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}