{"id":1011,"date":"2026-01-14T14:06:50","date_gmt":"2026-01-14T14:06:50","guid":{"rendered":"https:\/\/masterdesign.ubertconcepts.nl\/livingatlas\/?page_id=1011"},"modified":"2026-01-17T16:24:55","modified_gmt":"2026-01-17T16:24:55","slug":"experimental-digital-gameboard","status":"publish","type":"page","link":"https:\/\/masterdesign.ubertconcepts.nl\/livingatlas\/index.php\/master-design\/game-living-without-letters\/experimental-digital-gameboard\/","title":{"rendered":"Experimental digital gameboard"},"content":{"rendered":"\n<div id=\"la_game_root\"><\/div>\n\n<style>\n  #la_game_root{font-family:system-ui,-apple-system,Segoe UI,Roboto,Arial,sans-serif}\n\n  .la_card{\n    border:1px solid #eee;border-radius:14px;background:#fff;\n    box-shadow:0 1px 2px rgba(0,0,0,.04);\n    padding:12px;margin-top:12px\n  }\n  .la_title{font-weight:950;font-size:18px}\n  .la_sub{font-size:12px;color:#444;max-width:980px;margin-top:6px;line-height:1.4}\n  .la_btns{display:flex;gap:8px;flex-wrap:wrap;margin-top:10px}\n  .la_btn{\n    border:1px solid #ddd;background:#fff;padding:10px 12px;border-radius:10px;\n    cursor:pointer;font-size:13px;box-shadow:0 1px 2px rgba(0,0,0,.05);\n    color:#000 !important;\n  }\n  .la_btn:hover{background:#fafafa}\n  .la_small{font-size:12px;color:#000 !important;line-height:1.35}\n  .la_big{font-size:28px;font-weight:950;line-height:1;color:#000 !important}\n\n  \/* Full-width board block 30% smaller *\/\n  .la_boardBlock{\n    width:70vw;               \/* 30% smaller *\/\n    max-width:70vw;\n    margin-left:calc(50% - 35vw); \/* centered *\/\n    margin-right:calc(50% - 35vw);\n  }\n\n  \/* On smaller screens, go back to almost full width *\/\n  @media (max-width: 1100px){\n    .la_boardBlock{\n      width:96vw;\n      max-width:96vw;\n      margin-left:calc(50% - 48vw);\n      margin-right:calc(50% - 48vw);\n    }\n  }\n\n  .la_boardCard{\n    border:1px solid #eee;border-radius:14px;background:#fff;\n    box-shadow:0 1px 2px rgba(0,0,0,.04);\n    overflow:hidden;\n  }\n  .la_boardWrap{position:relative;width:100%;background:#f7f7f7}\n  .la_boardImg{width:100%;height:auto;display:block;user-select:none;-webkit-user-drag:none}\n  .la_hotspots,.la_pawns{position:absolute;inset:0;pointer-events:none}\n\n  \/* Click spots (comfortable but not huge) *\/\n  .la_spot{\n    position:absolute;transform:translate(-50%,-50%);\n    width:46px;height:46px;border-radius:999px;border:3px solid rgba(0,0,0,.35);\n    display:flex;align-items:center;justify-content:center;\n    font-size:14px;font-weight:950;\n    pointer-events:auto;cursor:pointer;\n    background:rgba(255,255,255,.75);backdrop-filter:blur(2px);\n    box-shadow:0 2px 4px rgba(0,0,0,.12);\n    color:#000 !important;\n  }\n  .la_spot[data-color=\"red\"]{background:rgba(220,38,38,.25)}\n  .la_spot[data-color=\"grey\"]{background:rgba(107,114,128,.25)}\n  .la_spot[data-color=\"yellow\"]{background:rgba(234,179,8,.25)}\n  .la_spot[data-color=\"orange\"]{background:rgba(249,115,22,.25)}\n  .la_spot[data-color=\"blue\"]{background:rgba(59,130,246,.25)}\n  .la_spot.reachable{outline:4px solid rgba(0,0,0,.12)}\n  .la_spot.blocked{opacity:.35;cursor:not-allowed}\n\n  .la_pawn{\n    position:absolute;transform:translate(-50%,-105%);\n    width:44px;height:44px;pointer-events:none;\n    filter:drop-shadow(0 2px 2px rgba(0,0,0,.25));\n  }\n\n  \/* Layout under board *\/\n  .la_grid{\n    display:grid;\n    grid-template-columns: 1fr 420px;\n    gap:14px;\n    align-items:start;\n    margin-top:12px;\n  }\n  @media (max-width:1100px){.la_grid{grid-template-columns:1fr}}\n  .la_box{margin-top:10px;border:1px dashed #ddd;border-radius:12px;padding:10px;background:#fff}\n\n  \/* Black text in inputs *\/\n  input, textarea, select { color:#000 !important; }\n  input::placeholder, textarea::placeholder { color:#444 !important; opacity:1; }\n\n  .la_playerCard{border:1px solid #eee;border-radius:12px;background:#fafafa;padding:10px;margin-top:10px}\n<\/style>\n\n<script>\n(() => {\n  \/\/ ======= YOUR BOARD_DATA (as provided) =======\n  const BOARD_DATA = {\n    \"boardImage\": \"https:\/\/masterdesign.ubertconcepts.nl\/livingatlas\/wp-content\/uploads\/2026\/01\/Speelbord-17-dec-2025-5-Speelbord-9-v03-12.png\",\n    \"createdAt\": \"2026-01-09T17:16:15.725Z\",\n    \"spots\": [\n      {\"i\":1,\"x\":0.056293,\"y\":0.895371,\"color\":\"blue\"},\n      {\"i\":2,\"x\":0.05948,\"y\":0.756397,\"color\":\"red\"},\n      {\"i\":3,\"x\":0.056824,\"y\":0.606155,\"color\":\"grey\"},\n      {\"i\":4,\"x\":0.055231,\"y\":0.476196,\"color\":\"none\"},\n      {\"i\":5,\"x\":0.0547,\"y\":0.36051,\"color\":\"yellow\"},\n      {\"i\":6,\"x\":0.055231,\"y\":0.260599,\"color\":\"none\"},\n      {\"i\":7,\"x\":0.054169,\"y\":0.14341,\"color\":\"grey\"},\n      {\"i\":8,\"x\":0.112055,\"y\":0.081811,\"color\":\"yellow\"},\n      {\"i\":9,\"x\":0.172066,\"y\":0.177966,\"color\":\"none\"},\n      {\"i\":10,\"x\":0.162507,\"y\":0.292901,\"color\":\"orange\"},\n      {\"i\":11,\"x\":0.165162,\"y\":0.411592,\"color\":\"blue\"},\n      {\"i\":12,\"x\":0.165693,\"y\":0.509249,\"color\":\"red\"},\n      {\"i\":13,\"x\":0.167286,\"y\":0.612165,\"color\":\"none\"},\n      {\"i\":14,\"x\":0.166224,\"y\":0.717334,\"color\":\"orange\"},\n      {\"i\":15,\"x\":0.170473,\"y\":0.817996,\"color\":\"none\"},\n      {\"i\":16,\"x\":0.183218,\"y\":0.897624,\"color\":\"yellow\"},\n      {\"i\":17,\"x\":0.260223,\"y\":0.902132,\"color\":\"none\"},\n      {\"i\":18,\"x\":0.269782,\"y\":0.799216,\"color\":\"none\"},\n      {\"i\":19,\"x\":0.263941,\"y\":0.706817,\"color\":\"red\"},\n      {\"i\":20,\"x\":0.270844,\"y\":0.586624,\"color\":\"yellow\"},\n      {\"i\":21,\"x\":0.266596,\"y\":0.500235,\"color\":\"none\"},\n      {\"i\":22,\"x\":0.26872,\"y\":0.380793,\"color\":\"orange\"},\n      {\"i\":23,\"x\":0.270844,\"y\":0.277126,\"color\":\"grey\"},\n      {\"i\":24,\"x\":0.269782,\"y\":0.173459,\"color\":\"blue\"},\n      {\"i\":25,\"x\":0.306426,\"y\":0.086319,\"color\":\"none\"},\n      {\"i\":26,\"x\":0.371747,\"y\":0.176464,\"color\":\"yellow\"},\n      {\"i\":27,\"x\":0.375465,\"y\":0.272618,\"color\":\"red\"},\n      {\"i\":28,\"x\":0.373871,\"y\":0.377036,\"color\":\"none\"},\n      {\"i\":29,\"x\":0.374934,\"y\":0.495727,\"color\":\"none\"},\n      {\"i\":30,\"x\":0.373871,\"y\":0.610662,\"color\":\"orange\"},\n      {\"i\":31,\"x\":0.372809,\"y\":0.712076,\"color\":\"grey\"},\n      {\"i\":32,\"x\":0.371216,\"y\":0.819499,\"color\":\"none\"},\n      {\"i\":33,\"x\":0.411577,\"y\":0.902132,\"color\":\"none\"},\n      {\"i\":34,\"x\":0.476899,\"y\":0.859313,\"color\":\"none\"},\n      {\"i\":35,\"x\":0.47265,\"y\":0.755646,\"color\":\"red\"},\n      {\"i\":36,\"x\":0.476367,\"y\":0.654233,\"color\":\"none\"},\n      {\"i\":37,\"x\":0.475305,\"y\":0.528781,\"color\":\"yellow\"},\n      {\"i\":38,\"x\":0.475305,\"y\":0.398822,\"color\":\"blue\"},\n      {\"i\":39,\"x\":0.475836,\"y\":0.299662,\"color\":\"grey\"},\n      {\"i\":40,\"x\":0.478492,\"y\":0.187732,\"color\":\"none\"},\n      {\"i\":41,\"x\":0.499203,\"y\":0.080309,\"color\":\"red\"},\n      {\"i\":42,\"x\":0.591078,\"y\":0.085567,\"color\":\"blue\"},\n      {\"i\":43,\"x\":0.614445,\"y\":0.179469,\"color\":\"none\"},\n      {\"i\":44,\"x\":0.61179,\"y\":0.277126,\"color\":\"orange\"},\n      {\"i\":45,\"x\":0.610728,\"y\":0.401075,\"color\":\"yellow\"},\n      {\"i\":46,\"x\":0.612852,\"y\":0.541551,\"color\":\"none\"},\n      {\"i\":47,\"x\":0.622411,\"y\":0.660993,\"color\":\"grey\"},\n      {\"i\":48,\"x\":0.705789,\"y\":0.693295,\"color\":\"blue\"},\n      {\"i\":49,\"x\":0.739777,\"y\":0.577609,\"color\":\"orange\"},\n      {\"i\":50,\"x\":0.734466,\"y\":0.443143,\"color\":\"red\"},\n      {\"i\":51,\"x\":0.732342,\"y\":0.294403,\"color\":\"none\"},\n      {\"i\":52,\"x\":0.731811,\"y\":0.17421,\"color\":\"yellow\"},\n      {\"i\":53,\"x\":0.78598,\"y\":0.080309,\"color\":\"none\"},\n      {\"i\":54,\"x\":0.851301,\"y\":0.145664,\"color\":\"red\"},\n      {\"i\":55,\"x\":0.858736,\"y\":0.270365,\"color\":\"blue\"},\n      {\"i\":56,\"x\":0.858736,\"y\":0.394314,\"color\":\"yellow\"},\n      {\"i\":57,\"x\":0.866702,\"y\":0.527278,\"color\":\"grey\"},\n      {\"i\":58,\"x\":0.866702,\"y\":0.630194,\"color\":\"none\"},\n      {\"i\":59,\"x\":0.869888,\"y\":0.768416,\"color\":\"orange\"},\n      {\"i\":60,\"x\":0.922995,\"y\":0.887859,\"color\":\"blue\"}\n    ]\n  };\n\n  \/\/ ======= Game config =======\n  const STORE_KEY = \"LA_GAME_STATE_v1\";\n  const ROOT_ID = \"la_game_root\";\n  const TASKS_URL = \"https:\/\/masterdesign.ubertconcepts.nl\/livingatlas\/index.php\/master-design\/spel-leven-zonder-letters\/taakkaarten\/\";\n\n  const PAWNS = [\n    { id:\"p1\", label:\"Pawn 1\", src:\"https:\/\/masterdesign.ubertconcepts.nl\/livingatlas\/wp-content\/uploads\/2026\/01\/Pion-1.png\" },\n    { id:\"p2\", label:\"Pawn 2\", src:\"https:\/\/masterdesign.ubertconcepts.nl\/livingatlas\/wp-content\/uploads\/2026\/01\/Pion-2.png\" },\n    { id:\"p3\", label:\"Pawn 3\", src:\"https:\/\/masterdesign.ubertconcepts.nl\/livingatlas\/wp-content\/uploads\/2026\/01\/Pion-3.png\" },\n    { id:\"p4\", label:\"Pawn 4\", src:\"https:\/\/masterdesign.ubertconcepts.nl\/livingatlas\/wp-content\/uploads\/2026\/01\/Pion-4.png\" },\n  ];\n\n  const TOKEN_IMG = \"https:\/\/masterdesign.ubertconcepts.nl\/livingatlas\/wp-content\/uploads\/2026\/01\/Tijd-fiche.png\";\n\n  const COLOR_LABEL = {\n    red: \"Help task cards\",\n    grey: \"Bad luck task cards\",\n    yellow: \"Position task cards\",\n    orange: \"Good luck task cards\",\n    blue: \"Scenario task cards\",\n  };\n\n  \/\/ Keywords to detect accordion sections (best-effort)\n  const ACC_MATCH = {\n    red:    [\"Help task cards\",\"Help\",\"Hulp taakkaarten\",\"Hulp\"],\n    grey:   [\"Bad luck task cards\",\"Bad luck\",\"Pech taakkaarten\",\"Pech\"],\n    yellow: [\"Position task cards\",\"Position\",\"Plaats taakkaarten\",\"Plaats\"],\n    orange: [\"Good luck task cards\",\"Good luck\",\"Geluk taakkaarten\",\"Geluk\"],\n    blue:   [\"Scenario task cards\",\"Scenario\",\"Scenario taakkaarten\"],\n  };\n\n  \/\/ ======= Helpers =======\n  const root = document.getElementById(ROOT_ID);\n  if (!root) return;\n\n  function uid(){ return Math.random().toString(36).slice(2,10); }\n  function clamp(n,a,b){ return Math.max(a, Math.min(b,n)); }\n  function $(id){ return document.getElementById(id); }\n  function escapeHtml(s){\n    return String(s).replace(\/[&<>\"']\/g, c => ({'&':'&amp;','<':'&lt;','>':'&gt;','\"':'&quot;',\"'\":'&#39;'}[c]));\n  }\n\n  function defaultState(){\n    return {\n      players: [{ id: uid(), name:\"Player 1\", pawnId:\"p1\", pos:1, tokens:20 }],\n      activeIdx: 0,\n      lastColor: null,\n      tasksCache: { fetchedAt:null, byColor:{ red:[], grey:[], yellow:[], orange:[], blue:[] } }\n    };\n  }\n\n  let state = load() || defaultState();\n\n  function save(){ localStorage.setItem(STORE_KEY, JSON.stringify(state)); }\n  function load(){ try{ return JSON.parse(localStorage.getItem(STORE_KEY)); }catch(e){ return null; } }\n\n  function activePlayer(){ return state.players[state.activeIdx]; }\n  function getSpot(i){ return BOARD_DATA.spots.find(s => s.i === i) || null; }\n  function maxSpot(){ return Math.max(...BOARD_DATA.spots.map(s=>s.i)); }\n  function minSpot(){ return Math.min(...BOARD_DATA.spots.map(s=>s.i)); }\n\n  \/\/ ======= UI =======\n  root.innerHTML = `\n    <div class=\"la_card\">\n      <div class=\"la_title\">Online game board<\/div>\n      <div class=\"la_sub\">\n        Click a space to move. Each step costs 1 time token.\n        You may move at most as many spaces as the number of time tokens you have (choose strategically!).\n      <\/div>\n      <div class=\"la_btns\">\n        <button class=\"la_btn\" id=\"btnReset\" type=\"button\">\ud83e\uddf9 Reset game<\/button>\n        <button class=\"la_btn\" id=\"btnClearTask\" type=\"button\">\ud83e\uddfd Clear task card<\/button>\n        <button class=\"la_btn\" id=\"btnRedraw\" type=\"button\">\ud83c\udfb2 New card (same colour)<\/button>\n      <\/div>\n      <div class=\"la_small\" id=\"status\" style=\"margin-top:8px;\"><\/div>\n    <\/div>\n\n    <div class=\"la_boardBlock\">\n      <div class=\"la_boardCard\">\n        <div class=\"la_boardWrap\" id=\"boardWrap\">\n          <img id=\"boardImg\" class=\"la_boardImg\" alt=\"Game board\" crossorigin=\"anonymous\">\n          <div class=\"la_hotspots\" id=\"hotspots\"><\/div>\n          <div class=\"la_pawns\" id=\"pawns\"><\/div>\n        <\/div>\n      <\/div>\n    <\/div>\n\n    <div class=\"la_grid\">\n      <div>\n        <div class=\"la_card\">\n          <div style=\"font-weight:950\">Task card<\/div>\n          <div class=\"la_small\">On a coloured space, a random card is drawn from the correct accordion section.<\/div>\n          <div class=\"la_box\" id=\"taskBox\"><span class=\"la_small\" style=\"color:#666\">No task card yet.<\/span><\/div>\n          <div id=\"audioWrap\" style=\"margin-top:10px;display:none\">\n            <audio id=\"audio\" controls preload=\"metadata\" style=\"width:100%\"><\/audio>\n          <\/div>\n        <\/div>\n      <\/div>\n\n      <div>\n        <div class=\"la_card\">\n          <div style=\"font-weight:950\">Players<\/div>\n          <div id=\"players\"><\/div>\n          <div style=\"display:flex;gap:8px;margin-top:10px;flex-wrap:wrap\">\n            <button class=\"la_btn\" id=\"btnAdd\" type=\"button\">\u2795 Add player<\/button>\n            <button class=\"la_btn\" id=\"btnNext\" type=\"button\">\u27a1\ufe0f Next<\/button>\n          <\/div>\n          <div class=\"la_card\" style=\"margin-top:10px;background:#f6f6f6\">\n            <div id=\"turnInfo\"><\/div>\n          <\/div>\n        <\/div>\n\n        <div class=\"la_card\">\n          <div style=\"font-weight:950\">Time tokens<\/div>\n          <div style=\"display:flex;gap:10px;align-items:center;margin-top:10px\">\n            <img decoding=\"async\" src=\"${TOKEN_IMG}\" alt=\"Time token\" style=\"width:44px;height:44px\">\n            <div>\n              <div class=\"la_small\">Current player<\/div>\n              <div class=\"la_big\" id=\"tokenCount\">20<\/div>\n            <\/div>\n          <\/div>\n\n          <div class=\"la_small\" style=\"margin-top:10px\">Adjust (e.g. 3 or -2)<\/div>\n          <div style=\"display:flex;gap:8px;margin-top:6px\">\n            <input id=\"delta\" type=\"number\" step=\"1\" value=\"0\">\n            <button class=\"la_btn\" id=\"btnDelta\" type=\"button\">Apply<\/button>\n          <\/div>\n          <div class=\"la_small\" id=\"note\" style=\"margin-top:8px;\"><\/div>\n        <\/div>\n      <\/div>\n    <\/div>\n\n    <div id=\"modal\" style=\"display:none\"><\/div>\n  `;\n\n  const statusEl = $(\"status\");\n  const boardImg = $(\"boardImg\");\n  const boardWrap = $(\"boardWrap\");\n  const hotspots = $(\"hotspots\");\n  const pawnsLayer = $(\"pawns\");\n\n  const taskBox = $(\"taskBox\");\n  const audioWrap = $(\"audioWrap\");\n  const audioEl = $(\"audio\");\n\n  const playersEl = $(\"players\");\n  const turnInfo = $(\"turnInfo\");\n  const tokenCount = $(\"tokenCount\");\n  const delta = $(\"delta\");\n  const note = $(\"note\");\n\n  const btnReset = $(\"btnReset\");\n  const btnClearTask = $(\"btnClearTask\");\n  const btnRedraw = $(\"btnRedraw\");\n  const btnAdd = $(\"btnAdd\");\n  const btnNext = $(\"btnNext\");\n  const btnDelta = $(\"btnDelta\");\n\n  const modal = $(\"modal\");\n\n  function setStatus(msg){ statusEl.textContent = msg; }\n\n  \/\/ ======= Board rendering =======\n  function boardRect(){ return boardImg.getBoundingClientRect(); }\n  function pxFromRel(spot){\n    const r = boardRect();\n    return { x: spot.x * r.width, y: spot.y * r.height };\n  }\n\n  function renderHotspots(){\n    hotspots.innerHTML = \"\";\n    const r = boardRect();\n    if (!r.width || !r.height) return;\n\n    const p = activePlayer();\n    const maxMove = p.tokens;\n\n    const spotsSorted = BOARD_DATA.spots.slice().sort((a,b)=>a.i-b.i);\n\n    for (const s of spotsSorted){\n      const el = document.createElement(\"div\");\n      el.className = \"la_spot\";\n      el.dataset.color = s.color || \"none\";\n      el.textContent = s.i;\n\n      const steps = Math.abs(p.pos - s.i);\n      const reachable = steps <= maxMove;\n      if (reachable) el.classList.add(\"reachable\");\n      else el.classList.add(\"blocked\");\n\n      const pt = pxFromRel(s);\n      el.style.left = pt.x + \"px\";\n      el.style.top  = pt.y + \"px\";\n\n      el.addEventListener(\"click\", (ev) => {\n        ev.stopPropagation();\n        if (!reachable) {\n          note.textContent = `Too far: ${steps} steps, but you have ${maxMove} time tokens.`;\n          return;\n        }\n        moveTo(s.i);\n      });\n\n      hotspots.appendChild(el);\n    }\n  }\n\n  function renderPawns(){\n    pawnsLayer.innerHTML = \"\";\n    const r = boardRect();\n    if (!r.width || !r.height) return;\n\n    state.players.forEach((pl, idx) => {\n      const cfg = PAWNS.find(p => p.id === pl.pawnId) || PAWNS[0];\n      const img = document.createElement(\"img\");\n      img.className = \"la_pawn\";\n      img.src = cfg.src;\n      img.alt = cfg.label;\n\n      const s = getSpot(pl.pos);\n      if (s){\n        const pt = pxFromRel(s);\n        img.style.left = (pt.x + idx*10) + \"px\";\n        img.style.top  = (pt.y + idx*6) + \"px\";\n      } else {\n        img.style.left = \"20px\";\n        img.style.top  = (60 + idx*52) + \"px\";\n      }\n      pawnsLayer.appendChild(img);\n    });\n  }\n\n  \/\/ ======= Players UI =======\n  function renderPlayers(){\n    playersEl.innerHTML = \"\";\n    state.players.forEach((pl, idx) => {\n      const card = document.createElement(\"div\");\n      card.className = \"la_playerCard\";\n      card.innerHTML = `\n        <div style=\"display:flex;justify-content:space-between;gap:10px;align-items:center\">\n          <span class=\"la_small\"><b>${idx===state.activeIdx ? \"Your turn\" : \"Waiting\"}<\/b><\/span>\n          <button class=\"la_btn\" type=\"button\" data-del=\"${idx}\" ${state.players.length<=1?\"disabled\":\"\"}>\ud83d\uddd1\ufe0f<\/button>\n        <\/div>\n        <div style=\"margin-top:8px\">\n          <input type=\"text\" value=\"${escapeHtml(pl.name)}\">\n        <\/div>\n        <div style=\"margin-top:8px\">\n          <select>\n            ${PAWNS.map(p => `<option value=\"${p.id}\" ${p.id===pl.pawnId?\"selected\":\"\"}>${p.label}<\/option>`).join(\"\")}\n          <\/select>\n        <\/div>\n        <div class=\"la_small\" style=\"margin-top:8px\">\n          Position: <b>${pl.pos}<\/b> \u2022 Time tokens: <b>${pl.tokens}<\/b>\n        <\/div>\n      `;\n\n      const nameInput = card.querySelector(\"input\");\n      nameInput.addEventListener(\"input\", () => { pl.name = nameInput.value; save(); renderTurn(); });\n\n      const sel = card.querySelector(\"select\");\n      sel.addEventListener(\"change\", () => { pl.pawnId = sel.value; save(); renderPawns(); });\n\n      const delBtn = card.querySelector(\"[data-del]\");\n      delBtn?.addEventListener(\"click\", () => {\n        if (state.players.length <= 1) return;\n        state.players.splice(idx, 1);\n        state.activeIdx = clamp(state.activeIdx, 0, state.players.length - 1);\n        save();\n        renderAll();\n      });\n\n      playersEl.appendChild(card);\n    });\n  }\n\n  function renderTurn(){\n    const p = activePlayer();\n    turnInfo.innerHTML = `Current turn: <b>${escapeHtml(p.name || \"Player\")}<\/b> \u2022 Space: <b>${p.pos}<\/b>`;\n    tokenCount.textContent = String(p.tokens);\n  }\n\n  \/\/ ======= Task loading (best-effort, same as earlier) =======\n  async function ensureTasksLoaded(){\n    const hasAny = Object.values(state.tasksCache.byColor).some(arr => arr && arr.length);\n    if (hasAny) return;\n\n    setStatus(\"Loading task cards\u2026\");\n    try{\n      const res = await fetch(TASKS_URL, { credentials:\"omit\" });\n      const html = await res.text();\n      const doc = new DOMParser().parseFromString(html, \"text\/html\");\n\n      const byColor = { red:[], grey:[], yellow:[], orange:[], blue:[] };\n      const text = (el) => (el?.textContent || \"\").trim();\n\n      function findTitleNode(keywords){\n        const candidates = Array.from(doc.querySelectorAll(\n          \"button, h1, h2, h3, h4, .elementor-tab-title, .elementor-accordion-title, .elementor-toggle-title\"\n        ));\n        for (const kw of keywords){\n          const hit = candidates.find(el => text(el).toLowerCase().includes(kw.toLowerCase()));\n          if (hit) return hit;\n        }\n        return null;\n      }\n\n      function contentForTitle(titleNode){\n        if (!titleNode) return null;\n        const ctrl = titleNode.getAttribute?.(\"aria-controls\");\n        if (ctrl){\n          const c = doc.getElementById(ctrl);\n          if (c) return c;\n        }\n        let el = titleNode;\n        for (let k=0;k<6;k++){\n          if (!el.nextElementSibling) break;\n          el = el.nextElementSibling;\n          if (el.matches?.(\"[role='region'], .elementor-tab-content, .elementor-accordion-content, .elementor-toggle-content\")) return el;\n          if (text(el).length > 80) return el;\n        }\n        const item = titleNode.closest?.(\".elementor-accordion-item, .elementor-toggle-item\");\n        if (item){\n          const c = item.querySelector(\".elementor-tab-content, .elementor-accordion-content, .elementor-toggle-content\");\n          if (c) return c;\n        }\n        return null;\n      }\n\n      function findAudioUrl(scope){\n        if (!scope) return null;\n        const src1 = scope.querySelector(\"audio source\")?.getAttribute(\"src\");\n        if (src1) return src1;\n        const src2 = scope.querySelector(\"audio\")?.getAttribute(\"src\");\n        if (src2) return src2;\n        const link = Array.from(scope.querySelectorAll(\"a\"))\n          .map(a => a.getAttribute(\"href\"))\n          .find(h => h && \/\\.(mp3|wav|ogg)(\\?.*)?$\/i.test(h));\n        return link || null;\n      }\n\n      function extractCards(contentEl){\n        if (!contentEl) return [];\n        \/\/ Prefer paragraphs\/lis as cards\n        const blocks = Array.from(contentEl.querySelectorAll(\"p, li, div\"))\n          .map(el => ({ el, t: text(el) }))\n          .filter(x => x.t.length >= 25);\n\n        const seen = new Set();\n        const cards = [];\n        for (const b of blocks){\n          const key = b.t.slice(0,90).toLowerCase();\n          if (seen.has(key)) continue;\n          seen.add(key);\n          cards.push({ text:b.t, html:b.el.outerHTML, audio: findAudioUrl(b.el) });\n        }\n        return cards.slice(0, 180);\n      }\n\n      for (const [color, keywords] of Object.entries(ACC_MATCH)){\n        const title = findTitleNode(keywords);\n        const content = contentForTitle(title);\n        byColor[color] = extractCards(content);\n      }\n\n      state.tasksCache.byColor = byColor;\n      state.tasksCache.fetchedAt = new Date().toISOString();\n      save();\n\n      const counts = Object.entries(byColor).map(([c,a])=>`${c}:${a.length}`).join(\" \u2022 \");\n      setStatus(`Task cards loaded (${counts}).`);\n    }catch(e){\n      console.error(e);\n      setStatus(\"Could not load task cards (CORS\/page structure). The game still works.\");\n    }\n  }\n\n  function findAudioInHtml(html){\n    if (!html) return null;\n    const m = html.match(\/https?:\\\/\\\/[^\"'\\s>]+?\\.(mp3|wav|ogg)(\\?[^\"'\\s>]*)?\/i);\n    return m ? m[0] : null;\n  }\n\n  function pickTask(color){\n    const arr = state.tasksCache.byColor[color] || [];\n    if (!arr.length) return null;\n    return arr[Math.floor(Math.random() * arr.length)];\n  }\n\n  function showModal(title, sub, html){\n    modal.style.display = \"flex\";\n    modal.style.position = \"fixed\";\n    modal.style.inset = \"0\";\n    modal.style.background = \"rgba(0,0,0,.45)\";\n    modal.style.zIndex = \"9999\";\n    modal.style.alignItems = \"center\";\n    modal.style.justifyContent = \"center\";\n    modal.style.padding = \"18px\";\n\n    const audioUrl = findAudioInHtml(html);\n    modal.innerHTML = `\n      <div style=\"width:min(860px,100%);max-height:85vh;overflow:auto;background:#fff;border-radius:16px;box-shadow:0 20px 60px rgba(0,0,0,.25)\">\n        <div style=\"display:flex;justify-content:space-between;gap:10px;align-items:flex-start;padding:12px 12px 0\">\n          <div>\n            <div style=\"font-weight:950;font-size:16px\">${escapeHtml(title)}<\/div>\n            <div class=\"la_small\">${escapeHtml(sub || \"\")}<\/div>\n          <\/div>\n          <button class=\"la_btn\" id=\"modalClose\" type=\"button\">\u2715<\/button>\n        <\/div>\n        <div style=\"padding:12px\">${html || \"\"}<\/div>\n        ${audioUrl ? `\n          <div style=\"padding:0 12px 12px 12px\">\n            <div class=\"la_small\"><b>Audio<\/b><\/div>\n            <audio controls preload=\"metadata\" style=\"width:100%\"><source src=\"${audioUrl}\"><\/audio>\n          <\/div>` : \"\"\n        }\n      <\/div>\n    `;\n    $(\"modalClose\").addEventListener(\"click\", () => { modal.style.display=\"none\"; modal.innerHTML=\"\"; });\n    modal.addEventListener(\"click\", (e) => { if (e.target === modal) { modal.style.display=\"none\"; modal.innerHTML=\"\"; } }, { once:true });\n  }\n\n  function showTask(color, pos){\n    if (color === \"none\") return;\n    ensureTasksLoaded().then(() => {\n      const task = pickTask(color);\n      if (!task){\n        taskBox.innerHTML = `<span class=\"la_small\" style=\"color:#666\">No card found for ${escapeHtml(COLOR_LABEL[color] || color)}.<\/span>`;\n        audioWrap.style.display = \"none\";\n        showModal(\"No task card found\", `${COLOR_LABEL[color] || color} \u2022 space ${pos}`, `<p>No cards found for this accordion section.<\/p>`);\n        return;\n      }\n\n      taskBox.innerHTML = task.html || `<p>${escapeHtml(task.text || \"\")}<\/p>`;\n      const audioUrl = task.audio || findAudioInHtml(task.html);\n\n      if (audioUrl){\n        audioWrap.style.display = \"block\";\n        audioEl.src = audioUrl;\n        audioEl.load();\n      } else {\n        audioWrap.style.display = \"none\";\n        audioEl.pause();\n        audioEl.removeAttribute(\"src\");\n        audioEl.load();\n      }\n\n      showModal(\"Task card\", `${COLOR_LABEL[color] || color} \u2022 space ${pos}`, `<div>${task.html || \"\"}<\/div>`);\n    });\n  }\n\n  \/\/ ======= Gameplay =======\n  async function land(pos){\n    const s = getSpot(pos);\n    const color = s?.color || \"none\";\n    state.lastColor = (color !== \"none\") ? color : null;\n    save();\n\n    if (color === \"none\"){\n      setStatus(`Landed on space ${pos}.`);\n      return;\n    }\n    setStatus(`Landed on space ${pos} \u2192 ${COLOR_LABEL[color] || color}.`);\n    showTask(color, pos);\n  }\n\n  function moveTo(target){\n    const minI = minSpot(), maxI = maxSpot();\n    target = clamp(target, minI, maxI);\n\n    const p = activePlayer();\n    const cost = Math.abs(p.pos - target);\n\n    if (cost > p.tokens){\n      note.textContent = `Too far: costs ${cost}, but you have ${p.tokens}.`;\n      return;\n    }\n\n    p.pos = target;\n    p.tokens = Math.max(0, p.tokens - cost);\n    note.textContent = cost ? `Moved to ${target} (cost ${cost}).` : `You stayed on ${target}.`;\n\n    save();\n    renderPawns();\n    renderPlayers();\n    renderTurn();\n    renderHotspots();\n    land(target);\n  }\n\n  function applyDelta(){\n    const p = activePlayer();\n    const d = parseInt(delta.value, 10) || 0;\n    p.tokens = Math.max(0, p.tokens + d);\n    delta.value = \"0\";\n    note.textContent = `Time tokens adjusted: ${d >= 0 ? \"+\" : \"\"}${d}.`;\n    save();\n    renderPlayers();\n    renderTurn();\n    renderHotspots();\n  }\n\n  function nextTurn(){\n    state.activeIdx = (state.activeIdx + 1) % state.players.length;\n    note.textContent = \"\";\n    save();\n    renderTurn();\n    renderPlayers();\n    renderHotspots();\n    setStatus(`Next player: ${escapeHtml(activePlayer().name || \"Player\")}.`);\n  }\n\n  function addPlayer(){\n    if (state.players.length >= 4) return;\n    const used = new Set(state.players.map(p=>p.pawnId));\n    const free = PAWNS.find(x => !used.has(x.id)) || PAWNS[0];\n    state.players.push({ id: uid(), name:`Player ${state.players.length+1}`, pawnId: free.id, pos: 1, tokens: 20 });\n    save();\n    renderAll();\n  }\n\n  function resetGame(){\n    if (!confirm(\"Reset game? (players\/tokens\/positions\/cards)\")) return;\n    state = defaultState();\n    save();\n    clearTask();\n    renderAll();\n    setStatus(\"Game reset.\");\n  }\n\n  function clearTask(){\n    taskBox.innerHTML = `<span class=\"la_small\" style=\"color:#666\">No task card yet.<\/span>`;\n    audioWrap.style.display = \"none\";\n    audioEl.pause();\n    audioEl.removeAttribute(\"src\");\n    audioEl.load();\n    state.lastColor = null;\n    save();\n  }\n\n  function redraw(){\n    if (!state.lastColor){\n      showModal(\"No colour known\",\"Land on a coloured space first\",`<p>Land on a coloured space first to draw a card.<\/p>`);\n      return;\n    }\n    showTask(state.lastColor, activePlayer().pos);\n  }\n\n  \/\/ ======= Wire buttons =======\n  btnReset.addEventListener(\"click\", resetGame);\n  btnClearTask.addEventListener(\"click\", clearTask);\n  btnRedraw.addEventListener(\"click\", redraw);\n  btnAdd.addEventListener(\"click\", addPlayer);\n  btnNext.addEventListener(\"click\", nextTurn);\n  btnDelta.addEventListener(\"click\", applyDelta);\n\n  \/\/ ======= Init render =======\n  function renderAll(){\n    boardImg.src = BOARD_DATA.boardImage;\n    renderHotspots();\n    renderPawns();\n    renderPlayers();\n    renderTurn();\n  }\n\n  const ro = new ResizeObserver(() => { renderHotspots(); renderPawns(); });\n  ro.observe(boardWrap);\n  boardImg.addEventListener(\"load\", () => { renderHotspots(); renderPawns(); });\n\n  renderAll();\n  ensureTasksLoaded().catch(()=>{});\n})();\n<\/script>\n","protected":false},"excerpt":{"rendered":"","protected":false},"author":2,"featured_media":0,"parent":527,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"footnotes":""},"class_list":["post-1011","page","type-page","status-publish","hentry"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.4 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Experimental digital gameboard - Tanja Ubert - Master Design Research<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/masterdesign.ubertconcepts.nl\/livingatlas\/index.php\/master-design\/game-living-without-letters\/experimental-digital-gameboard\/\" \/>\n<meta property=\"og:locale\" content=\"en_GB\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Experimental digital gameboard - Tanja Ubert - Master Design Research\" \/>\n<meta property=\"og:url\" content=\"https:\/\/masterdesign.ubertconcepts.nl\/livingatlas\/index.php\/master-design\/game-living-without-letters\/experimental-digital-gameboard\/\" \/>\n<meta property=\"og:site_name\" content=\"Tanja Ubert - Master Design Research\" \/>\n<meta property=\"article:modified_time\" content=\"2026-01-17T16:24:55+00:00\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/masterdesign.ubertconcepts.nl\\\/livingatlas\\\/index.php\\\/master-design\\\/game-living-without-letters\\\/experimental-digital-gameboard\\\/\",\"url\":\"https:\\\/\\\/masterdesign.ubertconcepts.nl\\\/livingatlas\\\/index.php\\\/master-design\\\/game-living-without-letters\\\/experimental-digital-gameboard\\\/\",\"name\":\"Experimental digital gameboard - Tanja Ubert - Master Design Research\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/masterdesign.ubertconcepts.nl\\\/livingatlas\\\/#website\"},\"datePublished\":\"2026-01-14T14:06:50+00:00\",\"dateModified\":\"2026-01-17T16:24:55+00:00\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/masterdesign.ubertconcepts.nl\\\/livingatlas\\\/index.php\\\/master-design\\\/game-living-without-letters\\\/experimental-digital-gameboard\\\/#breadcrumb\"},\"inLanguage\":\"en-GB\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/masterdesign.ubertconcepts.nl\\\/livingatlas\\\/index.php\\\/master-design\\\/game-living-without-letters\\\/experimental-digital-gameboard\\\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/masterdesign.ubertconcepts.nl\\\/livingatlas\\\/index.php\\\/master-design\\\/game-living-without-letters\\\/experimental-digital-gameboard\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/masterdesign.ubertconcepts.nl\\\/livingatlas\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Master Design Research\",\"item\":\"https:\\\/\\\/masterdesign.ubertconcepts.nl\\\/livingatlas\\\/index.php\\\/master-design\\\/\"},{\"@type\":\"ListItem\",\"position\":3,\"name\":\"Game Living without Letters v10\",\"item\":\"https:\\\/\\\/masterdesign.ubertconcepts.nl\\\/livingatlas\\\/index.php\\\/master-design\\\/game-living-without-letters\\\/\"},{\"@type\":\"ListItem\",\"position\":4,\"name\":\"Experimental digital gameboard\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/masterdesign.ubertconcepts.nl\\\/livingatlas\\\/#website\",\"url\":\"https:\\\/\\\/masterdesign.ubertconcepts.nl\\\/livingatlas\\\/\",\"name\":\"Tanja Ubert - Master Design Research\",\"description\":\"What makes you weird is what makes you special - Meryl Streep\",\"publisher\":{\"@id\":\"https:\\\/\\\/masterdesign.ubertconcepts.nl\\\/livingatlas\\\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/masterdesign.ubertconcepts.nl\\\/livingatlas\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-GB\"},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/masterdesign.ubertconcepts.nl\\\/livingatlas\\\/#organization\",\"name\":\"Tanja Ubert - Master Design Research\",\"url\":\"https:\\\/\\\/masterdesign.ubertconcepts.nl\\\/livingatlas\\\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-GB\",\"@id\":\"https:\\\/\\\/masterdesign.ubertconcepts.nl\\\/livingatlas\\\/#\\\/schema\\\/logo\\\/image\\\/\",\"url\":\"https:\\\/\\\/masterdesign.ubertconcepts.nl\\\/livingatlas\\\/wp-content\\\/uploads\\\/2024\\\/06\\\/Helping-1-e1749551471217.png\",\"contentUrl\":\"https:\\\/\\\/masterdesign.ubertconcepts.nl\\\/livingatlas\\\/wp-content\\\/uploads\\\/2024\\\/06\\\/Helping-1-e1749551471217.png\",\"width\":2054,\"height\":2054,\"caption\":\"Tanja Ubert - Master Design Research\"},\"image\":{\"@id\":\"https:\\\/\\\/masterdesign.ubertconcepts.nl\\\/livingatlas\\\/#\\\/schema\\\/logo\\\/image\\\/\"}}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Experimental digital gameboard - Tanja Ubert - Master Design Research","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/masterdesign.ubertconcepts.nl\/livingatlas\/index.php\/master-design\/game-living-without-letters\/experimental-digital-gameboard\/","og_locale":"en_GB","og_type":"article","og_title":"Experimental digital gameboard - Tanja Ubert - Master Design Research","og_url":"https:\/\/masterdesign.ubertconcepts.nl\/livingatlas\/index.php\/master-design\/game-living-without-letters\/experimental-digital-gameboard\/","og_site_name":"Tanja Ubert - Master Design Research","article_modified_time":"2026-01-17T16:24:55+00:00","twitter_card":"summary_large_image","schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/masterdesign.ubertconcepts.nl\/livingatlas\/index.php\/master-design\/game-living-without-letters\/experimental-digital-gameboard\/","url":"https:\/\/masterdesign.ubertconcepts.nl\/livingatlas\/index.php\/master-design\/game-living-without-letters\/experimental-digital-gameboard\/","name":"Experimental digital gameboard - Tanja Ubert - Master Design Research","isPartOf":{"@id":"https:\/\/masterdesign.ubertconcepts.nl\/livingatlas\/#website"},"datePublished":"2026-01-14T14:06:50+00:00","dateModified":"2026-01-17T16:24:55+00:00","breadcrumb":{"@id":"https:\/\/masterdesign.ubertconcepts.nl\/livingatlas\/index.php\/master-design\/game-living-without-letters\/experimental-digital-gameboard\/#breadcrumb"},"inLanguage":"en-GB","potentialAction":[{"@type":"ReadAction","target":["https:\/\/masterdesign.ubertconcepts.nl\/livingatlas\/index.php\/master-design\/game-living-without-letters\/experimental-digital-gameboard\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/masterdesign.ubertconcepts.nl\/livingatlas\/index.php\/master-design\/game-living-without-letters\/experimental-digital-gameboard\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/masterdesign.ubertconcepts.nl\/livingatlas\/"},{"@type":"ListItem","position":2,"name":"Master Design Research","item":"https:\/\/masterdesign.ubertconcepts.nl\/livingatlas\/index.php\/master-design\/"},{"@type":"ListItem","position":3,"name":"Game Living without Letters v10","item":"https:\/\/masterdesign.ubertconcepts.nl\/livingatlas\/index.php\/master-design\/game-living-without-letters\/"},{"@type":"ListItem","position":4,"name":"Experimental digital gameboard"}]},{"@type":"WebSite","@id":"https:\/\/masterdesign.ubertconcepts.nl\/livingatlas\/#website","url":"https:\/\/masterdesign.ubertconcepts.nl\/livingatlas\/","name":"Tanja Ubert - Master Design Research","description":"What makes you weird is what makes you special - Meryl Streep","publisher":{"@id":"https:\/\/masterdesign.ubertconcepts.nl\/livingatlas\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/masterdesign.ubertconcepts.nl\/livingatlas\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-GB"},{"@type":"Organization","@id":"https:\/\/masterdesign.ubertconcepts.nl\/livingatlas\/#organization","name":"Tanja Ubert - Master Design Research","url":"https:\/\/masterdesign.ubertconcepts.nl\/livingatlas\/","logo":{"@type":"ImageObject","inLanguage":"en-GB","@id":"https:\/\/masterdesign.ubertconcepts.nl\/livingatlas\/#\/schema\/logo\/image\/","url":"https:\/\/masterdesign.ubertconcepts.nl\/livingatlas\/wp-content\/uploads\/2024\/06\/Helping-1-e1749551471217.png","contentUrl":"https:\/\/masterdesign.ubertconcepts.nl\/livingatlas\/wp-content\/uploads\/2024\/06\/Helping-1-e1749551471217.png","width":2054,"height":2054,"caption":"Tanja Ubert - Master Design Research"},"image":{"@id":"https:\/\/masterdesign.ubertconcepts.nl\/livingatlas\/#\/schema\/logo\/image\/"}}]}},"_links":{"self":[{"href":"https:\/\/masterdesign.ubertconcepts.nl\/livingatlas\/index.php\/wp-json\/wp\/v2\/pages\/1011","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/masterdesign.ubertconcepts.nl\/livingatlas\/index.php\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/masterdesign.ubertconcepts.nl\/livingatlas\/index.php\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/masterdesign.ubertconcepts.nl\/livingatlas\/index.php\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/masterdesign.ubertconcepts.nl\/livingatlas\/index.php\/wp-json\/wp\/v2\/comments?post=1011"}],"version-history":[{"count":1,"href":"https:\/\/masterdesign.ubertconcepts.nl\/livingatlas\/index.php\/wp-json\/wp\/v2\/pages\/1011\/revisions"}],"predecessor-version":[{"id":1012,"href":"https:\/\/masterdesign.ubertconcepts.nl\/livingatlas\/index.php\/wp-json\/wp\/v2\/pages\/1011\/revisions\/1012"}],"up":[{"embeddable":true,"href":"https:\/\/masterdesign.ubertconcepts.nl\/livingatlas\/index.php\/wp-json\/wp\/v2\/pages\/527"}],"wp:attachment":[{"href":"https:\/\/masterdesign.ubertconcepts.nl\/livingatlas\/index.php\/wp-json\/wp\/v2\/media?parent=1011"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}