{"id":58,"date":"2026-10-21T00:06:42","date_gmt":"2026-10-21T00:06:42","guid":{"rendered":"https:\/\/apk.produsenpagarbrc.com\/?p=58"},"modified":"2026-10-21T00:06:42","modified_gmt":"2026-10-21T00:06:42","slug":"honeybound-tiles","status":"publish","type":"post","link":"https:\/\/play.produsenpagarbrc.com\/?p=58","title":{"rendered":"Honeybound Tiles"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\"><strong>Honeybound Tiles<\/strong> is a cozy Mahjong Solitaire puzzle game set inside a warm honeylit library, where every tile feels like a tiny piece of an old golden archive. Books, honey jars, teacups, bookmarks, reading glasses, keys, quills, candles, bees, and honeycomb pages are stacked across the board, waiting to be uncovered and matched.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Your goal is simple: find two matching open tiles and clear them from the shelf. A tile can only be selected when it is not covered by another tile above it and has at least one free side. Clear the stack layer by layer, protect your time, and restore every shelf in the Honey Library.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" src=\"https:\/\/play.produsenpagarbrc.com\/wp-content\/uploads\/2026\/10\/Honeybound-Tiles-1024x768.png\" alt=\"\" class=\"wp-image-59\"\/><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">A Cozy Mahjong Puzzle in a Honeylit Library<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Honeybound Tiles<\/strong> brings the classic Mahjong Solitaire experience into a softer and more magical world. Instead of rushing through a cold board, you enter a quiet reading room filled with amber light, warm wooden shelves, parchment colors, floating golden dust, and charming library objects.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Each level gives you a layered tile stack to solve. Some tiles are open and ready to match, while others stay hidden beneath the upper layers. The challenge is to think carefully, remove the right pairs, and slowly reveal the pieces underneath.<\/p>\n\n\n\n<style>\n  .clm01-shell{\n    width:100%;\n    max-width:980px;\n    margin:18px auto;\n    position:relative;\n    font-family:Arial,Helvetica,sans-serif;\n    user-select:none;\n    -webkit-user-select:none;\n    -webkit-tap-highlight-color:transparent;\n  }\n\n  .clm01-stage{\n    --edge:clamp(10px,1.7vw,18px);\n    --icon:clamp(40px,4.7vw,50px);\n    width:100%;\n    aspect-ratio:16\/9;\n    position:relative;\n    overflow:hidden;\n    border-radius:24px;\n    background:\n      radial-gradient(circle at 82% 14%,rgba(255,244,220,.70),transparent 24%),\n      linear-gradient(180deg,#8ec9e6 0%,#d8f3f6 48%,#fff4dc 100%);\n    box-shadow:0 22px 50px rgba(72,106,134,.24);\n    touch-action:none;\n  }\n\n  .clm01-shell:fullscreen{\n    width:100vw;\n    height:100vh;\n    max-width:none;\n    margin:0;\n    padding:0;\n    background:#7cbbd9;\n    display:flex;\n    align-items:center;\n    justify-content:center;\n  }\n\n  .clm01-shell:fullscreen .clm01-stage{\n    width:min(100vw,calc(100vh * 16 \/ 9));\n    height:min(100vh,calc(100vw * 9 \/ 16));\n    border-radius:0;\n  }\n\n  .clm01-shell.clm01-portrait-full:fullscreen .clm01-stage{\n    width:min(100vh,calc(100vw * 16 \/ 9));\n    height:min(100vw,calc(100vh * 9 \/ 16));\n    transform:rotate(90deg);\n    transform-origin:center center;\n  }\n\n  .clm01-stage canvas{\n    position:absolute;\n    inset:0;\n    width:100%;\n    height:100%;\n    display:block;\n  }\n\n  .clm01-top-left,\n  .clm01-top-right,\n  .clm01-top-center,\n  .clm01-bottom{\n    position:absolute;\n    z-index:7;\n  }\n\n  .clm01-top-left{\n    top:var(--edge);\n    left:var(--edge);\n    display:flex;\n    gap:8px;\n  }\n\n  .clm01-top-right{\n    top:var(--edge);\n    right:var(--edge);\n    z-index:35;\n  }\n\n  .clm01-top-center{\n    top:var(--edge);\n    left:50%;\n    transform:translateX(-50%);\n    width:min(47%,500px);\n    min-height:var(--icon);\n    display:flex;\n    flex-direction:column;\n    align-items:center;\n    justify-content:center;\n    gap:4px;\n  }\n\n  .clm01-icon-btn{\n    width:var(--icon);\n    height:var(--icon);\n    border:1px solid rgba(72,106,134,.24);\n    border-radius:15px;\n    background:rgba(249,252,255,.56);\n    backdrop-filter:blur(8px);\n    cursor:pointer;\n    display:grid;\n    place-items:center;\n    padding:0;\n    box-shadow:0 8px 16px rgba(72,106,134,.16);\n    transition:transform .14s ease,background .14s ease,border-color .14s ease;\n  }\n\n  .clm01-icon-btn:hover{\n    background:rgba(255,244,220,.72);\n    border-color:rgba(72,106,134,.38);\n  }\n\n  .clm01-icon-btn:active{\n    transform:scale(.94);\n  }\n\n  .clm01-speaker-icon,\n  .clm01-pause-icon,\n  .clm01-full-icon{\n    position:relative;\n    width:24px;\n    height:24px;\n    display:block;\n  }\n\n  .clm01-speaker-box{\n    position:absolute;\n    left:2px;\n    top:9px;\n    width:7px;\n    height:7px;\n    background:#486a86;\n    border-radius:2px;\n  }\n\n  .clm01-speaker-cone{\n    position:absolute;\n    left:8px;\n    top:5px;\n    width:10px;\n    height:15px;\n    background:#486a86;\n    clip-path:polygon(0 32%,100% 0,100% 100%,0 68%);\n    border-radius:2px;\n  }\n\n  .clm01-wave{\n    position:absolute;\n    border:2.3px solid #486a86;\n    border-left:0;\n    border-top-color:transparent;\n    border-bottom-color:transparent;\n    border-radius:0 999px 999px 0;\n  }\n\n  .clm01-wave.w1{\n    width:5px;\n    height:8px;\n    left:16px;\n    top:8px;\n    opacity:.9;\n  }\n\n  .clm01-wave.w2{\n    width:8px;\n    height:13px;\n    left:18px;\n    top:5.5px;\n    opacity:.75;\n  }\n\n  .clm01-wave.w3{\n    width:11px;\n    height:18px;\n    left:20px;\n    top:3px;\n    opacity:.55;\n  }\n\n  .clm01-slash{\n    display:none;\n    position:absolute;\n    width:27px;\n    height:3px;\n    left:-1px;\n    top:11px;\n    background:#486a86;\n    border-radius:999px;\n    transform:rotate(-42deg);\n  }\n\n  .clm01-muted .clm01-wave{display:none;}\n  .clm01-muted .clm01-slash{display:block;}\n\n  .clm01-pause-icon:before,\n  .clm01-pause-icon:after{\n    content:\"\";\n    position:absolute;\n    top:4px;\n    width:6px;\n    height:16px;\n    background:#486a86;\n    border-radius:3px;\n  }\n\n  .clm01-pause-icon:before{left:5px;}\n  .clm01-pause-icon:after{right:5px;}\n\n  .clm01-full-icon:before,\n  .clm01-full-icon:after,\n  .clm01-full-icon span:before,\n  .clm01-full-icon span:after{\n    content:\"\";\n    position:absolute;\n    width:8px;\n    height:8px;\n    border-color:#486a86;\n    border-style:solid;\n  }\n\n  .clm01-full-icon:before{\n    left:2px;\n    top:2px;\n    border-width:3px 0 0 3px;\n    border-radius:3px 0 0 0;\n  }\n\n  .clm01-full-icon:after{\n    right:2px;\n    top:2px;\n    border-width:3px 3px 0 0;\n    border-radius:0 3px 0 0;\n  }\n\n  .clm01-full-icon span:before{\n    left:2px;\n    bottom:2px;\n    border-width:0 0 3px 3px;\n    border-radius:0 0 0 3px;\n  }\n\n  .clm01-full-icon span:after{\n    right:2px;\n    bottom:2px;\n    border-width:0 3px 3px 0;\n    border-radius:0 0 3px 0;\n  }\n\n  .clm01-hud{\n    width:100%;\n    display:grid;\n    grid-template-columns:repeat(5,1fr);\n    gap:4px;\n  }\n\n  .clm01-hud-item{\n    min-height:34px;\n    padding:2px 5px 3px;\n    border-radius:11px;\n    background:linear-gradient(180deg,rgba(249,252,255,.84),rgba(216,243,246,.74));\n    border:1px solid rgba(72,106,134,.25);\n    box-shadow:0 7px 13px rgba(72,106,134,.13);\n    backdrop-filter:blur(8px);\n    display:flex;\n    flex-direction:column;\n    align-items:center;\n    justify-content:center;\n    line-height:1;\n    white-space:nowrap;\n  }\n\n  .clm01-label{\n    font-size:8px;\n    letter-spacing:.04em;\n    color:rgba(72,106,134,.68);\n    font-weight:900;\n    line-height:1;\n  }\n\n  .clm01-value{\n    margin-top:1px;\n    font-size:clamp(11px,1.28vw,15px);\n    font-weight:950;\n    line-height:1;\n    text-shadow:0 2px 6px rgba(0,0,0,.18);\n  }\n\n  .clm01-hud-item:nth-child(1) .clm01-value{color:#5fbfae;}\n  .clm01-hud-item:nth-child(2) .clm01-value{color:#e79b73;}\n  .clm01-hud-item:nth-child(3) .clm01-value{color:#9e8dd8;}\n  .clm01-hud-item:nth-child(4) .clm01-value{color:#5c9fc6;}\n  .clm01-hud-item:nth-child(5) .clm01-value{color:#486a86;}\n\n  .clm01-timebar{\n    width:100%;\n    height:8px;\n    padding:1px;\n    border-radius:999px;\n    background:rgba(72,106,134,.18);\n    border:1px solid rgba(72,106,134,.18);\n    box-shadow:inset 0 1px 3px rgba(0,0,0,.16);\n    overflow:hidden;\n  }\n\n  .clm01-timefill{\n    width:100%;\n    height:100%;\n    border-radius:999px;\n    background:linear-gradient(90deg,#8fd8c8,#8ec9e6);\n    transition:width .2s linear,background .2s ease;\n  }\n\n  .clm01-timebar.warning .clm01-timefill{\n    background:linear-gradient(90deg,#f4b38d,#e97878);\n  }\n\n  .clm01-time-warning .clm01-time-text{\n    animation:clm01PulseTime .72s ease-in-out infinite alternate;\n  }\n\n  @keyframes clm01PulseTime{\n    from{text-shadow:0 0 6px rgba(244,179,141,.35);}\n    to{text-shadow:0 0 18px rgba(233,120,120,.78);}\n  }\n\n  .clm01-bottom{\n    left:50%;\n    bottom:clamp(12px,2.2vw,22px);\n    transform:translateX(-50%);\n    display:flex;\n    gap:clamp(12px,2vw,22px);\n  }\n\n  .clm01-action{\n    min-width:clamp(100px,13vw,150px);\n    height:clamp(40px,5vw,54px);\n    border:1px solid rgba(72,106,134,.22);\n    border-radius:18px;\n    background:linear-gradient(180deg,rgba(188,239,230,.88),rgba(117,201,184,.86));\n    color:#244e5e;\n    font-size:clamp(13px,1.55vw,17px);\n    font-weight:900;\n    letter-spacing:.04em;\n    cursor:pointer;\n    box-shadow:0 12px 22px rgba(72,106,134,.18);\n    backdrop-filter:blur(8px);\n    transition:transform .14s ease,filter .14s ease;\n  }\n\n  .clm01-action:hover{filter:brightness(1.05);}\n  .clm01-action:active{transform:scale(.96);}\n\n  .clm01-bottom .clm01-action:nth-child(2){\n    background:linear-gradient(180deg,#ffd1b8,#f4a47c);\n    color:#5a3f42;\n  }\n\n  .clm01-muted .clm01-slash{\n    background:#e97878;\n  }\n\n  .clm01-overlay{\n    position:absolute;\n    inset:0;\n    z-index:20;\n    display:none;\n    align-items:center;\n    justify-content:center;\n    background:rgba(98,155,188,.28);\n    backdrop-filter:blur(4px);\n    padding:20px;\n  }\n\n  .clm01-overlay.show{display:flex;}\n\n  .clm01-card{\n    width:min(360px,68%);\n    border-radius:20px;\n    padding:clamp(14px,2.1vw,22px);\n    text-align:center;\n    background:\n      radial-gradient(circle at 18% 12%,rgba(255,244,220,.58),transparent 34%),\n      linear-gradient(180deg,rgba(249,252,255,.96),rgba(216,243,246,.94));\n    border:1px solid rgba(72,106,134,.25);\n    box-shadow:0 18px 42px rgba(72,106,134,.25);\n    color:#486a86;\n  }\n\n  .clm01-card h2{\n    margin:0 0 11px;\n    font-size:clamp(16px,2.15vw,23px);\n    letter-spacing:.06em;\n    color:#486a86;\n  }\n\n  .clm01-card p{\n    margin:11px auto 0;\n    color:rgba(72,106,134,.82);\n    font-size:clamp(11px,1.25vw,13px);\n    line-height:1.35;\n    max-width:280px;\n  }\n\n  .clm01-menu{\n    display:grid;\n    gap:8px;\n    justify-items:center;\n  }\n\n  .clm01-menu-btn{\n    width:min(205px,88%);\n    min-height:38px;\n    border:0;\n    border-radius:14px;\n    background:linear-gradient(180deg,#ffd1b8,#f4a47c);\n    color:#5a3f42;\n    font-weight:950;\n    letter-spacing:.04em;\n    font-size:clamp(11px,1.32vw,14px);\n    cursor:pointer;\n    box-shadow:0 9px 16px rgba(6,8,22,.2);\n    transition:transform .14s ease,filter .14s ease;\n  }\n\n  .clm01-menu-btn.secondary{\n    background:linear-gradient(180deg,#bcefe6,#75c9b8);\n    color:#244e5e;\n  }\n\n  .clm01-menu-btn.ghost{\n    background:rgba(249,252,255,.54);\n    color:#486a86;\n    border:1px solid rgba(72,106,134,.22);\n  }\n\n  .clm01-menu-btn:active{transform:scale(.96);}\n\n  @media (max-width:640px){\n    .clm01-shell{\n      max-width:100%;\n      margin:10px auto;\n    }\n\n    .clm01-stage{\n      --icon:clamp(38px,7vw,46px);\n      border-radius:16px;\n    }\n\n    .clm01-top-left{gap:7px;}\n\n    .clm01-top-center{\n      width:min(52%,400px);\n      gap:3px;\n      min-height:var(--icon);\n    }\n\n    .clm01-hud{gap:3px;}\n\n    .clm01-hud-item{\n      min-height:28px;\n      padding:1px 2px 2px;\n      border-radius:9px;\n    }\n\n    .clm01-label{\n      font-size:6.2px;\n      letter-spacing:.025em;\n    }\n\n    .clm01-value{\n      font-size:10.5px;\n      margin-top:1px;\n    }\n\n    .clm01-timebar{\n      height:6px;\n      padding:1px;\n    }\n\n    .clm01-icon-btn{border-radius:14px;}\n\n    .clm01-card{\n      width:min(330px,72%);\n      border-radius:17px;\n      padding:13px 14px 15px;\n    }\n\n    .clm01-card h2{\n      margin-bottom:9px;\n      font-size:15px;\n    }\n\n    .clm01-card p{\n      margin-top:9px;\n      font-size:10.5px;\n      line-height:1.3;\n      max-width:240px;\n    }\n\n    .clm01-menu{gap:7px;}\n\n    .clm01-menu-btn{\n      width:min(185px,88%);\n      min-height:34px;\n      border-radius:12px;\n      font-size:11px;\n    }\n\n    .clm01-bottom{bottom:10px;}\n  }\n\n\n  \/* =========================================================\n     Honeybound Tiles - Honey Library final redesign layer\n     Visual skin only. Core Mahjong Solitaire logic remains intact.\n     ========================================================= *\/\n  .clm01-shell{\n    color:#5A321F !important;\n  }\n\n  .clm01-stage{\n    background:\n      radial-gradient(circle at 82% 12%, rgba(255,248,223,.76), transparent 23%),\n      radial-gradient(circle at 16% 16%, rgba(244,197,66,.24), transparent 24%),\n      radial-gradient(circle at 72% 86%, rgba(184,115,51,.22), transparent 30%),\n      linear-gradient(150deg,#2B1710 0%,#5A321F 38%,#9B6426 70%,#FFF1C9 100%) !important;\n    border:1px solid rgba(255,241,201,.48) !important;\n    box-shadow:0 24px 58px rgba(90,50,31,.30), inset 0 0 0 1px rgba(255,248,223,.16) !important;\n  }\n\n  .clm01-stage:before{\n    content:\"\";\n    position:absolute;\n    inset:0;\n    z-index:0;\n    pointer-events:none;\n    opacity:.62;\n    background:\n      radial-gradient(ellipse at 50% -12%, rgba(255,248,223,.38), transparent 42%),\n      repeating-linear-gradient(90deg, rgba(255,241,201,.045) 0 1px, transparent 1px 34px),\n      repeating-linear-gradient(0deg, rgba(244,197,66,.028) 0 1px, transparent 1px 30px);\n  }\n\n  .clm01-stage canvas,\n  .clm01-top-left,\n  .clm01-top-right,\n  .clm01-top-center,\n  .clm01-bottom,\n  .clm01-overlay{position:absolute;}\n\n  .clm01-shell:fullscreen,\n  .clm01-shell.clm01-portrait-full:fullscreen{\n    background:#3F2418 !important;\n  }\n\n  .clm01-icon-btn{\n    background:linear-gradient(180deg,rgba(255,248,223,.90),rgba(255,241,201,.78)) !important;\n    border-color:rgba(90,50,31,.30) !important;\n    box-shadow:0 9px 20px rgba(63,36,24,.20), inset 0 1px 0 rgba(255,255,255,.46) !important;\n  }\n\n  .clm01-icon-btn:hover{\n    background:linear-gradient(180deg,rgba(255,248,223,.98),rgba(244,197,66,.44)) !important;\n    border-color:rgba(244,197,66,.54) !important;\n  }\n\n  .clm01-speaker-box,\n  .clm01-speaker-cone,\n  .clm01-pause-icon:before,\n  .clm01-pause-icon:after,\n  .clm01-slash{background:#5A321F !important;}\n\n  .clm01-wave{border-color:#5A321F !important;border-left:0 !important;border-top-color:transparent !important;border-bottom-color:transparent !important;}\n  .clm01-muted .clm01-slash{background:#C2412D !important;}\n\n  .clm01-full-icon:before,\n  .clm01-full-icon:after,\n  .clm01-full-icon span:before,\n  .clm01-full-icon span:after{border-color:#5A321F !important;}\n\n  .clm01-hud-item{\n    background:linear-gradient(180deg,rgba(255,248,223,.92),rgba(255,241,201,.80)) !important;\n    border-color:rgba(90,50,31,.27) !important;\n    box-shadow:0 8px 17px rgba(63,36,24,.17), inset 0 1px 0 rgba(255,255,255,.40) !important;\n  }\n\n  .clm01-label{color:rgba(90,50,31,.66) !important;}\n  .clm01-value{color:#5A321F !important;text-shadow:0 1px 4px rgba(255,248,223,.36) !important;}\n  .clm01-hud-item:nth-child(1) .clm01-value{color:#9B6426 !important;}\n  .clm01-hud-item:nth-child(2) .clm01-value{color:#B87333 !important;}\n  .clm01-hud-item:nth-child(3) .clm01-value{color:#6F4128 !important;}\n  .clm01-hud-item:nth-child(4) .clm01-value{color:#8F6431 !important;}\n  .clm01-hud-item:nth-child(5) .clm01-value{color:#A74A30 !important;}\n\n  .clm01-timebar{\n    background:rgba(63,36,24,.30) !important;\n    border-color:rgba(255,241,201,.22) !important;\n    box-shadow:inset 0 1px 4px rgba(63,36,24,.28), 0 5px 13px rgba(63,36,24,.16) !important;\n  }\n\n  .clm01-timefill{\n    background:linear-gradient(90deg,#FFF1C9,#F4C542,#D9953F) !important;\n    box-shadow:0 0 16px rgba(244,197,66,.46) !important;\n  }\n\n  .clm01-timebar.warning .clm01-timefill{\n    background:linear-gradient(90deg,#F4C542,#D9953F,#C2412D) !important;\n  }\n\n  .clm01-action{\n    background:linear-gradient(180deg,#FFF8DF,#F4C542) !important;\n    color:#5A321F !important;\n    border-color:rgba(90,50,31,.24) !important;\n    box-shadow:0 12px 24px rgba(63,36,24,.22), inset 0 1px 0 rgba(255,255,255,.42) !important;\n  }\n\n  .clm01-bottom .clm01-action:nth-child(2){\n    background:linear-gradient(180deg,#D9953F,#9B6426) !important;\n    color:#FFF8DF !important;\n  }\n\n  .clm01-overlay{\n    background:rgba(63,36,24,.38) !important;\n    backdrop-filter:blur(8px) !important;\n  }\n\n  .clm01-card{\n    background:\n      radial-gradient(circle at 22% 14%,rgba(255,255,255,.64),transparent 30%),\n      linear-gradient(145deg,rgba(255,248,223,.98),rgba(255,241,201,.92)) !important;\n    border-color:rgba(255,248,223,.68) !important;\n    box-shadow:0 24px 58px rgba(63,36,24,.32), 0 0 28px rgba(244,197,66,.24), inset 0 0 0 1px rgba(255,255,255,.28) !important;\n    color:#5A321F !important;\n  }\n\n  .clm01-card h2{color:#3F2418 !important;letter-spacing:.045em !important;}\n  .clm01-card p{color:rgba(90,50,31,.78) !important;}\n\n  .clm01-menu-btn{\n    background:linear-gradient(180deg,#F4C542,#B87333) !important;\n    color:#FFF8DF !important;\n    box-shadow:0 10px 20px rgba(90,50,31,.26), inset 0 1px 0 rgba(255,255,255,.28) !important;\n  }\n\n  .clm01-menu-btn.secondary{\n    background:linear-gradient(180deg,#FFF8DF,#F4C542) !important;\n    color:#5A321F !important;\n  }\n\n  .clm01-menu-btn.ghost{\n    background:rgba(255,248,223,.66) !important;\n    color:#5A321F !important;\n    border-color:rgba(90,50,31,.22) !important;\n  }\n\n<\/style>\n\n<div class=\"clm01-shell\" id=\"clm01Shell\">\n  <div class=\"clm01-stage\" id=\"clm01Stage\">\n    <canvas id=\"clm01Canvas\"><\/canvas>\n\n    <div class=\"clm01-top-left\">\n      <button class=\"clm01-icon-btn\" id=\"clm01PauseBtn\" type=\"button\" aria-label=\"Pause\">\n        <span class=\"clm01-pause-icon\"><\/span>\n      <\/button>\n      <button class=\"clm01-icon-btn\" id=\"clm01SoundBtn\" type=\"button\" aria-label=\"Sound\">\n        <span class=\"clm01-speaker-icon\">\n          <span class=\"clm01-speaker-box\"><\/span>\n          <span class=\"clm01-speaker-cone\"><\/span>\n          <span class=\"clm01-wave w1\"><\/span>\n          <span class=\"clm01-wave w2\"><\/span>\n          <span class=\"clm01-wave w3\"><\/span>\n          <span class=\"clm01-slash\"><\/span>\n        <\/span>\n      <\/button>\n    <\/div>\n\n    <div class=\"clm01-top-center\">\n      <div class=\"clm01-hud\" id=\"clm01Hud\">\n        <div class=\"clm01-hud-item\"><span class=\"clm01-label\">LEVEL<\/span><span class=\"clm01-value\" id=\"clm01Level\">1<\/span><\/div>\n        <div class=\"clm01-hud-item\"><span class=\"clm01-label\">SCORE<\/span><span class=\"clm01-value\" id=\"clm01Score\">0<\/span><\/div>\n        <div class=\"clm01-hud-item\"><span class=\"clm01-label\">BEST<\/span><span class=\"clm01-value\" id=\"clm01Best\">0<\/span><\/div>\n        <div class=\"clm01-hud-item\"><span class=\"clm01-label\">MOVES<\/span><span class=\"clm01-value\" id=\"clm01Moves\">0<\/span><\/div>\n        <div class=\"clm01-hud-item\"><span class=\"clm01-label\">TIME<\/span><span class=\"clm01-value clm01-time-text\" id=\"clm01Time\">00:00<\/span><\/div>\n      <\/div>\n      <div class=\"clm01-timebar\" id=\"clm01TimeBar\">\n        <div class=\"clm01-timefill\" id=\"clm01TimeFill\"><\/div>\n      <\/div>\n    <\/div>\n\n    <div class=\"clm01-top-right\">\n      <button class=\"clm01-icon-btn\" id=\"clm01FullBtn\" type=\"button\" aria-label=\"Fullscreen\">\n        <span class=\"clm01-full-icon\"><span><\/span><\/span>\n      <\/button>\n    <\/div>\n\n    <div class=\"clm01-bottom\">\n      <button class=\"clm01-action\" id=\"clm01HintBtn\" type=\"button\">HINT<\/button>\n      <button class=\"clm01-action\" id=\"clm01ShuffleBtn\" type=\"button\">SHUFFLE TILES<\/button>\n    <\/div>\n\n    <div class=\"clm01-overlay\" id=\"clm01Overlay\"><\/div>\n  <\/div>\n<\/div>\n\n<script>\n(function(){\n  \"use strict\";\n\n  function initializeHoneyboundTiles(){\n\n  const W = 1280;\n  const H = 720;\n  const DPR_MAX = 2.75;\n  const FIXED_LAYERS = 3;\n\n  const shell = document.getElementById(\"clm01Shell\");\n  const canvas = document.getElementById(\"clm01Canvas\");\n  if (!shell || !canvas || shell.dataset.hbtWpReady === \"1\") return;\n  const ctx = canvas.getContext(\"2d\");\n  if (!ctx) return;\n  shell.dataset.hbtWpReady = \"1\";\n  const overlay = document.getElementById(\"clm01Overlay\");\n\n  const soundBtn = document.getElementById(\"clm01SoundBtn\");\n  const pauseBtn = document.getElementById(\"clm01PauseBtn\");\n  const fullBtn = document.getElementById(\"clm01FullBtn\");\n  const hintBtn = document.getElementById(\"clm01HintBtn\");\n  const shuffleBtn = document.getElementById(\"clm01ShuffleBtn\");\n  const hud = document.getElementById(\"clm01Hud\");\n  const timeBar = document.getElementById(\"clm01TimeBar\");\n  const timeFill = document.getElementById(\"clm01TimeFill\");\n\n  const levelEl = document.getElementById(\"clm01Level\");\n  const scoreEl = document.getElementById(\"clm01Score\");\n  const bestEl = document.getElementById(\"clm01Best\");\n  const movesEl = document.getElementById(\"clm01Moves\");\n  const timeEl = document.getElementById(\"clm01Time\");\n\n  function storageGet(key){try{return window.localStorage.getItem(key);}catch(error){return null;}}\n  function storageSet(key,value){try{window.localStorage.setItem(key,value);}catch(error){}}\n  function storageRemove(key){try{window.localStorage.removeItem(key);}catch(error){}}\n\n  const KEY_BEST = \"honeybound_tiles_best_v1\";\n  const KEY_SAVE = \"honeybound_tiles_progress_v1\";\n  const KEY_MUTE = \"honeybound_tiles_muted_v1\";\n\n  const SYMBOLS = [\n    \"book\",\"honeyjar\",\"teacup\",\"parchment\",\"bookmark\",\"glasses\",\"key\",\"bookstack\",\"quill\",\n    \"honeydrop\",\"ink\",\"lamp\",\"pageswirl\",\"bee\",\"waxribbon\",\"openbook\",\"honeycomb\",\"candle\"\n  ];\n\n  const LEVELS = [\n    24,26,28,30,32,34,36,38,40,42,\n    44,46,48,50,52,54,56,58,60,62,\n    64,66,68,70,72,74,76,78,80,82,\n    84,86,88,90,92,94,96,98,100,102,\n    104,106,108,110,112,114,116,118,120,122\n  ];\n\n  const state = {\n    mode:\"menu\",\n    level:1,\n    score:0,\n    best:Number(storageGet(KEY_BEST)) || 0,\n    moves:0,\n    timeLeft:0,\n    timeLimit:0,\n    tiles:[],\n    selectedId:null,\n    particles:[],\n    floats:[],\n    hintIds:[],\n    hintTimer:0,\n    levelIntro:0,\n    combo:0,\n    comboTimer:0,\n    usedHint:false,\n    usedShuffle:false,\n    pendingClear:false,\n    muted:storageGet(KEY_MUTE) === \"1\",\n    lastTickSecond:-1,\n    bounds:null,\n    rngSeed:Date.now()\n  };\n\n  let lastTime = performance.now();\n  let audioCtx = null;\n\n  function clamp(v,a,b){return Math.max(a,Math.min(b,v));}\n  function fmt(n){return Math.floor(n).toLocaleString(\"en-US\");}\n  function timeFormat(sec){\n    sec = Math.max(0,Math.ceil(sec));\n    const m = Math.floor(sec \/ 60);\n    const s = sec % 60;\n    return String(m).padStart(2,\"0\") + \":\" + String(s).padStart(2,\"0\");\n  }\n\n  function seeded(seed){\n    let t = seed >>> 0;\n    return function(){\n      t += 0x6D2B79F5;\n      let r = Math.imul(t ^ (t >>> 15),1 | t);\n      r ^= r + Math.imul(r ^ (r >>> 7),61 | r);\n      return ((r ^ (r >>> 14)) >>> 0) \/ 4294967296;\n    };\n  }\n\n  function shuffleArray(arr,rng){\n    for(let i = arr.length - 1; i > 0; i--){\n      const j = Math.floor(rng() * (i + 1));\n      const t = arr[i];\n      arr[i] = arr[j];\n      arr[j] = t;\n    }\n    return arr;\n  }\n\n  function resizeCanvas(){\n    const dpr = Math.min(window.devicePixelRatio || 1,DPR_MAX);\n    canvas.width = Math.round(W * dpr);\n    canvas.height = Math.round(H * dpr);\n    canvas.style.width = \"100%\";\n    canvas.style.height = \"100%\";\n    ctx.setTransform(dpr,0,0,dpr,0,0);\n    ctx.imageSmoothingEnabled = true;\n    ctx.imageSmoothingQuality = \"high\";\n  }\n\n  function updateFullscreenClass(){\n    const active = document.fullscreenElement === shell;\n    if(((__hbtAndValue) => __hbtAndValue ? (window.innerWidth < window.innerHeight) : __hbtAndValue)(active)){\n      shell.classList.add(\"clm01-portrait-full\");\n    }else{\n      shell.classList.remove(\"clm01-portrait-full\");\n    }\n    setTimeout(resizeCanvas,80);\n  }\n\n  function tryOrientationLock(){\n    if(((__hbtAndValue) => __hbtAndValue ? (screen.orientation.lock) : __hbtAndValue)(screen.orientation)){\n      screen.orientation.lock(\"landscape\").catch(function(){});\n    }\n  }\n\n  function unlockOrientation(){\n    if(((__hbtAndValue) => __hbtAndValue ? (screen.orientation.unlock) : __hbtAndValue)(screen.orientation)){\n      try{screen.orientation.unlock();}catch(e){}\n    }\n  }\n\n  function updateHud(){\n    levelEl.textContent = String(state.level);\n    scoreEl.textContent = fmt(state.score);\n    bestEl.textContent = fmt(state.best);\n    movesEl.textContent = fmt(state.moves);\n    timeEl.textContent = timeFormat(state.timeLeft);\n\n    const ratio = state.timeLimit > 0 ? clamp(state.timeLeft \/ state.timeLimit,0,1) : 0;\n    timeFill.style.width = (ratio * 100) + \"%\";\n\n    hud.classList.toggle(\"clm01-time-warning\",((__hbtAndValue) => __hbtAndValue ? (state.mode === \"playing\") : __hbtAndValue)(state.timeLeft <= 30));\n    timeBar.classList.toggle(\"warning\",((__hbtAndValue) => __hbtAndValue ? (state.mode === \"playing\") : __hbtAndValue)(state.timeLeft <= 30));\n    soundBtn.classList.toggle(\"clm01-muted\",state.muted);\n  }\n\n  function showOverlay(html){\n    overlay.innerHTML = html;\n    overlay.classList.add(\"show\");\n  }\n\n  function hideOverlay(){\n    overlay.classList.remove(\"show\");\n    overlay.innerHTML = \"\";\n  }\n\n  function bindOverlayButton(action,fn){\n    const btn = overlay.querySelector('[data-action=\"' + action + '\"]');\n    if(btn) btn.addEventListener(\"click\",fn);\n  }\n\n  function hasSavedProgress(){\n    try{\n      const raw = storageGet(KEY_SAVE);\n      if(!raw) return false;\n      const save = JSON.parse(raw);\n      return !!(((__hbtAndValue) => __hbtAndValue ? (Array.isArray(save.tiles)) : __hbtAndValue)(((__hbtAndValue) => __hbtAndValue ? (save.level) : __hbtAndValue)(save)));\n    }catch(e){\n      return false;\n    }\n  }\n\n  function showStartPopup(){\n    state.mode = \"menu\";\n    updateHud();\n\n    const continueBtn = hasSavedProgress()\n      ? '<button class=\"clm01-menu-btn secondary\" data-action=\"continue\">CONTINUE READING<\/button>'\n      : \"\";\n\n    showOverlay(\n      '<div class=\"clm01-card\">' +\n        '<h2>HONEYBOUND TILES<\/h2>' +\n        '<div class=\"clm01-menu\">' +\n          '<button class=\"clm01-menu-btn\" data-action=\"start\">OPEN THE ARCHIVE<\/button>' +\n          continueBtn +\n        '<\/div>' +\n        '<p>Match open library tiles only. Covered tiles stay hidden beneath the golden shelf.<\/p>' +\n      '<\/div>'\n    );\n\n    bindOverlayButton(\"start\",function(){\n      sound(\"button\");\n      startNewGame();\n    });\n\n    bindOverlayButton(\"continue\",function(){\n      sound(\"button\");\n      continueGame();\n    });\n  }\n\n  function showPausePopup(){\n    if(state.mode !== \"playing\") return;\n    state.mode = \"paused\";\n    saveProgress();\n\n    showOverlay(\n      '<div class=\"clm01-card\">' +\n        '<h2>READING BREAK<\/h2>' +\n        '<div class=\"clm01-menu\">' +\n          '<button class=\"clm01-menu-btn\" data-action=\"resume\">RETURN TO THE SHELF<\/button>' +\n          '<button class=\"clm01-menu-btn ghost\" data-action=\"saveGame\">SAVE GAME<\/button>' +\n          '<button class=\"clm01-menu-btn secondary\" data-action=\"confirmNew\">NEW ARCHIVE<\/button>' +\n        '<\/div>' +\n        '<p>The honeylit shelf is resting. Save your quiet puzzle and return anytime.<\/p>' +\n      '<\/div>'\n    );\n\n    bindOverlayButton(\"resume\",function(){\n      sound(\"button\");\n      hideOverlay();\n      state.mode = \"playing\";\n    });\n\n    bindOverlayButton(\"saveGame\",function(){\n      sound(\"button\");\n      saveProgress();\n      clearSelection();\n      state.mode = \"menu\";\n      showStartPopup();\n    });\n\n    bindOverlayButton(\"confirmNew\",function(){\n      sound(\"button\");\n      showNewGameConfirm();\n    });\n  }\n\n  function showNewGameConfirm(){\n    showOverlay(\n      '<div class=\"clm01-card\">' +\n        '<h2>NEW ARCHIVE?<\/h2>' +\n        '<div class=\"clm01-menu\">' +\n          '<button class=\"clm01-menu-btn ghost\" data-action=\"cancelNew\">CANCEL<\/button>' +\n          '<button class=\"clm01-menu-btn secondary\" data-action=\"newGame\">NEW ARCHIVE<\/button>' +\n        '<\/div>' +\n        '<p>A fresh archive will open, and the current honeybound shelf will be closed.<\/p>' +\n      '<\/div>'\n    );\n\n    bindOverlayButton(\"cancelNew\",function(){\n      sound(\"button\");\n      if(state.mode === \"paused\") showPausePopup();\n      else showStartPopup();\n    });\n\n    bindOverlayButton(\"newGame\",function(){\n      sound(\"button\");\n      startNewGame();\n    });\n  }\n\n  function showNoMovesPopup(){\n    if(state.mode !== \"playing\") return;\n\n    if(!canOfferShuffle()){\n      showGameOverPopup();\n      return;\n    }\n\n    state.mode = \"nomoves\";\n    saveProgress();\n\n    showOverlay(\n      '<div class=\"clm01-card\">' +\n        '<h2>NO OPEN SHELF<\/h2>' +\n        '<div class=\"clm01-menu\">' +\n          '<button class=\"clm01-menu-btn\" data-action=\"shuffleNow\">SHUFFLE TILES<\/button>' +\n          '<button class=\"clm01-menu-btn secondary\" data-action=\"confirmNew\">NEW ARCHIVE<\/button>' +\n        '<\/div>' +\n        '<p>The golden shelf has no open pair. Shuffle the archive and let the honeylight guide another move.<\/p>' +\n      '<\/div>'\n    );\n\n    bindOverlayButton(\"shuffleNow\",function(){\n      sound(\"button\");\n      hideOverlay();\n      state.mode = \"playing\";\n      shuffleTiles(true);\n    });\n\n    bindOverlayButton(\"confirmNew\",function(){\n      sound(\"button\");\n      showNewGameConfirm();\n    });\n  }\n\n  function showGameOverPopup(){\n    if(state.mode !== \"playing\") return;\n    state.mode = \"gameover\";\n    saveProgress();\n\n    showOverlay(\n      '<div class=\"clm01-card\">' +\n        '<h2>ARCHIVE SEALED<\/h2>' +\n        '<div class=\"clm01-menu\">' +\n          '<button class=\"clm01-menu-btn secondary\" data-action=\"newGame\">NEW ARCHIVE<\/button>' +\n        '<\/div>' +\n        '<p>No golden pair can be opened further. Begin a fresh archive.<\/p>' +\n      '<\/div>'\n    );\n\n    bindOverlayButton(\"newGame\",function(){\n      sound(\"button\");\n      startNewGame();\n    });\n  }\n\n  function showTimeUpPopup(){\n    state.mode = \"timeup\";\n    saveProgress();\n\n    showOverlay(\n      '<div class=\"clm01-card\">' +\n        '<h2>THE LAMP HAS DIMMED<\/h2>' +\n        '<div class=\"clm01-menu\">' +\n          '<button class=\"clm01-menu-btn\" data-action=\"retry\">RETRY SHELF<\/button>' +\n          '<button class=\"clm01-menu-btn secondary\" data-action=\"confirmNew\">NEW ARCHIVE<\/button>' +\n        '<\/div>' +\n        '<p>The reading lamp faded before the shelf was cleared. Try this level again.<\/p>' +\n      '<\/div>'\n    );\n\n    bindOverlayButton(\"retry\",function(){\n      sound(\"button\");\n      startLevel(state.level,true);\n    });\n\n    bindOverlayButton(\"confirmNew\",function(){\n      sound(\"button\");\n      showNewGameConfirm();\n    });\n  }\n\n  function showLevelClearPopup(){\n    state.mode = \"levelclear\";\n    saveProgress();\n\n    const isLast = state.level >= 50;\n    const title = isLast ? \"ARCHIVE COMPLETE\" : \"SHELF CLEARED\";\n    const action = isLast ? \"NEW ARCHIVE\" : \"NEXT SHELF\";\n    const note = isLast\n      ? \"Every honeybound tile has found its pair beneath the golden library light.\"\n      : \"Another golden shelf is restored. A new honeybound shelf is waiting.\";\n\n    showOverlay(\n      '<div class=\"clm01-card\">' +\n        '<h2>' + title + '<\/h2>' +\n        '<div class=\"clm01-menu\">' +\n          '<button class=\"clm01-menu-btn\" data-action=\"next\">' + action + '<\/button>' +\n        '<\/div>' +\n        '<p>' + note + '<\/p>' +\n      '<\/div>'\n    );\n\n    bindOverlayButton(\"next\",function(){\n      sound(\"button\");\n      if(isLast){\n        startNewGame();\n      }else{\n        state.level += 1;\n        startLevel(state.level,false);\n      }\n    });\n  }\n\n  function startNewGame(){\n    storageRemove(KEY_SAVE);\n    state.level = 1;\n    state.score = 0;\n    state.moves = 0;\n    state.rngSeed = Date.now() + Math.floor(Math.random() * 999999);\n    hideOverlay();\n    startLevel(1,false);\n  }\n\n  function continueGame(){\n    try{\n      const save = JSON.parse(storageGet(KEY_SAVE) || \"{}\");\n      if(!save || !Array.isArray(save.tiles)){\n        showStartPopup();\n        return;\n      }\n\n      state.level = clamp(save.level || 1,1,50);\n      state.score = Number(save.score) || 0;\n      state.moves = Number(save.moves) || 0;\n      state.timeLeft = Number(save.timeLeft) || 180;\n      state.timeLimit = Number(save.timeLimit) || state.timeLeft;\n      state.usedHint = !!save.usedHint;\n      state.usedShuffle = !!save.usedShuffle;\n      state.combo = 0;\n      state.comboTimer = 0;\n      state.hintIds = [];\n      state.hintTimer = 0;\n      state.particles = [];\n      state.floats = [];\n      state.pendingClear = false;\n      state.levelIntro = 1.2;\n      state.selectedId = null;\n      state.lastTickSecond = -1;\n\n      state.tiles = save.tiles.map(function(t,i){\n        return {\n          id:i + 1,\n          x:t.x,\n          y:t.y,\n          z:t.z,\n          symbol:t.symbol,\n          removed:!!t.removed,\n          selected:false,\n          removing:0,\n          deny:0,\n          wrong:0,\n          unlockGlow:0,\n          sx:0,\n          sy:0,\n          w:0,\n          h:0\n        };\n      });\n\n      state.bounds = save.bounds || makeBounds(state.tiles);\n      hideOverlay();\n      state.mode = \"playing\";\n      updateHud();\n    }catch(e){\n      showStartPopup();\n    }\n  }\n\n  function startLevel(level,retry){\n    state.mode = \"playing\";\n    state.level = level;\n    state.selectedId = null;\n    state.hintIds = [];\n    state.hintTimer = 0;\n    state.particles = [];\n    state.floats = [];\n    state.combo = 0;\n    state.comboTimer = 0;\n    state.usedHint = false;\n    state.usedShuffle = false;\n    state.pendingClear = false;\n    state.levelIntro = 1.35;\n    state.lastTickSecond = -1;\n\n    if(retry) state.moves = Math.max(0,state.moves);\n\n    const tileCount = LEVELS[level - 1];\n    state.timeLimit = Math.round(120 + tileCount * 4 + level * 2);\n    state.timeLeft = state.timeLimit;\n\n    generateLevel(level);\n    hideOverlay();\n    updateHud();\n    saveProgress();\n  }\n\n  function makeBounds(list){\n    const usable = list.filter(Boolean);\n\n    if(!usable.length) return {minX:-1,maxX:1,minY:-1,maxY:1,maxZ:2};\n\n    let minX = Infinity;\n    let maxX = -Infinity;\n    let minY = Infinity;\n    let maxY = -Infinity;\n    let maxZ = 0;\n\n    usable.forEach(function(t){\n      minX = Math.min(minX,t.x);\n      maxX = Math.max(maxX,t.x);\n      minY = Math.min(minY,t.y);\n      maxY = Math.max(maxY,t.y);\n      maxZ = Math.max(maxZ,t.z);\n    });\n\n    return {minX:minX,maxX:maxX,minY:minY,maxY:maxY,maxZ:maxZ};\n  }\n\n  function layerCounts(total){\n    let l1 = Math.max(8,Math.round(total * .52));\n    let l2 = Math.max(6,Math.round(total * .30));\n    let l3 = total - l1 - l2;\n\n    if(l3 < 4){\n      const need = 4 - l3;\n      l1 -= Math.ceil(need \/ 2);\n      l2 -= Math.floor(need \/ 2);\n      l3 = 4;\n    }\n\n    let diff = total - (l1 + l2 + l3);\n    l1 += diff;\n\n    return [l1,l2,l3];\n  }\n\n  function generateLayerPositions(count,z,level,rng,used){\n    const positions = [];\n    const maxCols = z === 0\n      ? Math.min(17,10 + Math.floor(level \/ 5))\n      : z === 1\n        ? Math.min(13,7 + Math.floor(level \/ 7))\n        : Math.min(9,4 + Math.floor(level \/ 10));\n\n    const rows = Math.max(2,Math.ceil(count \/ maxCols));\n    const cols = Math.ceil(count \/ rows);\n    const candidates = [];\n\n    for(let y = 0; y < rows; y++){\n      const rowExtra = z === 0 ? 2 : 1;\n      const rowCols = cols + rowExtra;\n      for(let x = 0; x < rowCols; x++){\n        const px = x - Math.floor(rowCols \/ 2);\n        const py = y - Math.floor(rows \/ 2);\n        const centerWeight = Math.abs(px) + Math.abs(py);\n\n        candidates.push({\n          x:px,\n          y:py,\n          score:z === 0 ? rng() * 5 + Math.abs(py) * .15 : centerWeight + rng() * .7\n        });\n      }\n    }\n\n    if(z === 0){\n      shuffleArray(candidates,rng);\n    }else{\n      candidates.sort(function(a,b){return a.score - b.score;});\n    }\n\n    for(let i = 0; ((__hbtAndValue) => __hbtAndValue ? (positions.length < count) : __hbtAndValue)(i < candidates.length); i++){\n      const c = candidates[i];\n      const key = c.x + \",\" + c.y + \",\" + z;\n\n      if(!used.has(key)){\n        used.add(key);\n        positions.push({x:c.x,y:c.y,z:z});\n      }\n    }\n\n    let guard = 0;\n    while(((__hbtAndValue) => __hbtAndValue ? (guard < 1000) : __hbtAndValue)(positions.length < count)){\n      const x = Math.floor(rng() * (cols + 2)) - Math.floor((cols + 2) \/ 2);\n      const y = Math.floor(rng() * rows) - Math.floor(rows \/ 2);\n      const key = x + \",\" + y + \",\" + z;\n\n      if(!used.has(key)){\n        used.add(key);\n        positions.push({x:x,y:y,z:z});\n      }\n\n      guard++;\n    }\n\n    return positions;\n  }\n\n  function createThreeLayerPositions(total,level,rng){\n    const counts = layerCounts(total);\n    const used = new Set();\n    let positions = [];\n\n    for(let z = 0; z < FIXED_LAYERS; z++){\n      positions = positions.concat(generateLayerPositions(counts[z],z,level,rng,used));\n    }\n\n    positions = positions.slice(0,total);\n\n    if(positions.length % 2 === 1) positions.pop();\n\n    return positions;\n  }\n\n  function createSafeThreeLayerPositions(total,rng){\n    const counts = layerCounts(total);\n    let positions = [];\n\n    counts.forEach(function(count,z){\n      const rows = z === 0 ? Math.max(2,Math.ceil(count \/ 16)) : z === 1 ? Math.max(2,Math.ceil(count \/ 12)) : Math.max(2,Math.ceil(count \/ 8));\n      const cols = Math.ceil(count \/ rows);\n\n      for(let i = 0; i < count; i++){\n        const row = Math.floor(i \/ cols);\n        const col = i % cols;\n        const items = Math.min(cols,count - row * cols);\n\n        positions.push({\n          x:col - Math.floor(items \/ 2),\n          y:row - Math.floor(rows \/ 2),\n          z:z\n        });\n      }\n    });\n\n    return shuffleArray(positions,rng).slice(0,total);\n  }\n\n  function generateLevel(level){\n    const tileCount = LEVELS[level - 1];\n    const baseSeed = (state.rngSeed + level * 99173 + Math.floor(Date.now() % 100000)) >>> 0;\n\n    for(let attempt = 0; attempt < 120; attempt++){\n      const rng = seeded((baseSeed + attempt * 17239) >>> 0);\n      const positions = attempt < 100\n        ? createThreeLayerPositions(tileCount,level,rng)\n        : createSafeThreeLayerPositions(tileCount,rng);\n\n      state.tiles = positions.map(function(p,i){\n        return {\n          id:i + 1,\n          x:p.x,\n          y:p.y,\n          z:p.z,\n          symbol:\"moon\",\n          removed:false,\n          selected:false,\n          removing:0,\n          deny:0,\n          wrong:0,\n          unlockGlow:0,\n          sx:0,\n          sy:0,\n          w:0,\n          h:0\n        };\n      });\n\n      state.bounds = makeBounds(state.tiles);\n      computeTileRects();\n\n      if(((__hbtAndValue) => __hbtAndValue ? (hasValidMove()) : __hbtAndValue)(assignSolvableSymbols(rng,level))){\n        return;\n      }\n    }\n\n    const fallbackRng = seeded((baseSeed + 999999) >>> 0);\n    const fallback = createSafeThreeLayerPositions(tileCount,fallbackRng);\n\n    state.tiles = fallback.map(function(p,i){\n      return {\n        id:i + 1,\n        x:p.x,\n        y:p.y,\n        z:p.z,\n        symbol:\"moon\",\n        removed:false,\n        selected:false,\n        removing:0,\n        deny:0,\n        wrong:0,\n        unlockGlow:0,\n        sx:0,\n        sy:0,\n        w:0,\n        h:0\n      };\n    });\n\n    state.bounds = makeBounds(state.tiles);\n    computeTileRects();\n    assignSolvableSymbols(fallbackRng,level);\n  }\n\n  function computeTileRects(){\n    if(!state.bounds) return;\n\n    const b = state.bounds;\n    const area = {x:88,y:108,w:1104,h:486};\n    const cols = Math.max(1,b.maxX - b.minX + 1);\n    const rows = Math.max(1,b.maxY - b.minY + 1);\n\n    let tileW = Math.min(58,area.w \/ (cols * 1.08 + .8));\n    let tileH = tileW * 1.5;\n\n    let stepX = tileW * 1.045;\n    let stepY = tileH * 1.025;\n\n    let depthX = tileW * .11;\n    let depthY = tileH * .085;\n\n    let totalW = (cols - 1) * stepX + tileW + 2 * depthX;\n    let totalH = (rows - 1) * stepY + tileH + 2 * depthY;\n    const sc = Math.min(1,area.w \/ totalW,area.h \/ totalH);\n\n    tileW *= sc;\n    tileH *= sc;\n    stepX *= sc;\n    stepY *= sc;\n    depthX *= sc;\n    depthY *= sc;\n\n    totalW = (cols - 1) * stepX + tileW + 2 * depthX;\n    totalH = (rows - 1) * stepY + tileH + 2 * depthY;\n\n    const startX = area.x + (area.w - totalW) \/ 2;\n    const startY = area.y + (area.h - totalH) \/ 2 + 2 * depthY;\n\n    state.tiles.forEach(function(t){\n      t.w = tileW;\n      t.h = tileH;\n      t.sx = startX + (t.x - b.minX) * stepX + t.z * depthX;\n      t.sy = startY + (t.y - b.minY) * stepY - t.z * depthY;\n    });\n  }\n\n  function assignSolvableSymbols(rng,level){\n    const originalRemoved = {};\n    const originalRemoving = {};\n    const assigned = {};\n    const symbolCount = Math.min(18,6 + Math.floor((level + 1) \/ 4));\n    let pairIndex = 0;\n    let guard = 0;\n\n    state.tiles.forEach(function(t){\n      originalRemoved[t.id] = !!t.removed;\n      originalRemoving[t.id] = t.removing || 0;\n      t.removing = 0;\n    });\n\n    while(true){\n      const remaining = state.tiles.filter(function(t){return !t.removed;});\n\n      if(!remaining.length) break;\n      if(remaining.length < 2 || guard++ > 500){\n        restore();\n        return false;\n      }\n\n      computeTileRects();\n\n      const free = remaining.filter(function(t){\n        return isFree(t);\n      });\n\n      if(free.length < 2){\n        restore();\n        return false;\n      }\n\n      shuffleArray(free,rng);\n\n      const a = free[0];\n      const b = free[1];\n      const symbol = SYMBOLS[(pairIndex + Math.floor(rng() * symbolCount)) % symbolCount];\n\n      assigned[a.id] = symbol;\n      assigned[b.id] = symbol;\n\n      a.removed = true;\n      b.removed = true;\n      pairIndex++;\n    }\n\n    state.tiles.forEach(function(t){\n      t.removed = originalRemoved[t.id];\n      t.removing = originalRemoving[t.id];\n\n      if(((__hbtAndValue) => __hbtAndValue ? (assigned[t.id]) : __hbtAndValue)(!t.removed)){\n        t.symbol = assigned[t.id];\n        t.selected = false;\n        t.deny = 0;\n        t.wrong = 0;\n      }\n    });\n\n    return true;\n\n    function restore(){\n      state.tiles.forEach(function(t){\n        t.removed = originalRemoved[t.id];\n        t.removing = originalRemoving[t.id];\n      });\n    }\n  }\n\n  function isActive(t){\n    return ((__hbtAndValue) => __hbtAndValue ? (!t.removing) : __hbtAndValue)(((__hbtAndValue) => __hbtAndValue ? (!t.removed) : __hbtAndValue)(t));\n  }\n\n  function hasRemovingTiles(){\n    return state.tiles.some(function(t){return t.removing > 0;});\n  }\n\n  function rectOverlapAmount(a,b){\n    const left = Math.max(a.x,b.x);\n    const right = Math.min(a.x + a.w,b.x + b.w);\n    const top = Math.max(a.y,b.y);\n    const bottom = Math.min(a.y + a.h,b.y + b.h);\n    const ow = right - left;\n    const oh = bottom - top;\n\n    if(ow <= 0 || oh <= 0) return 0;\n    return ow * oh;\n  }\n\n  function getTileCoverRect(t){\n    return {\n      x:t.sx + t.w * .04,\n      y:t.sy + t.h * .04,\n      w:t.w * .92,\n      h:t.h * .92\n    };\n  }\n\n  function isCovered(t){\n    if(!isActive(t)) return true;\n\n    if(!t.w || !t.h) computeTileRects();\n\n    const target = getTileCoverRect(t);\n    const targetArea = target.w * target.h;\n\n    return state.tiles.some(function(u){\n      if(!isActive(u)) return false;\n      if(u.z <= t.z) return false;\n\n      const upper = getTileCoverRect(u);\n      const overlap = rectOverlapAmount(target,upper);\n\n      return overlap > targetArea * .002;\n    });\n  }\n\n  function sideBlocked(t,dir){\n    if(!isActive(t)) return true;\n\n    return state.tiles.some(function(u){\n      if(!isActive(u)) return false;\n      if(u.z !== t.z) return false;\n\n      const sameRow = Math.abs(u.y - t.y) < .8;\n      const side = Math.abs(u.x - (t.x + dir)) < .45;\n\n      return ((__hbtAndValue) => __hbtAndValue ? (side) : __hbtAndValue)(sameRow);\n    });\n  }\n\n  function isFree(t){\n    if(!isActive(t)) return false;\n    if(isCovered(t)) return false;\n\n    const leftOpen = !sideBlocked(t,-1);\n    const rightOpen = !sideBlocked(t,1);\n\n    return leftOpen || rightOpen;\n  }\n\n  function activeTiles(){\n    return state.tiles.filter(isActive);\n  }\n\n  function getFreeTiles(){\n    computeTileRects();\n    return activeTiles().filter(isFree);\n  }\n\n  function canOfferShuffle(){\n    return getFreeTiles().length >= 2;\n  }\n\n  function findValidPairs(){\n    const free = getFreeTiles();\n    const groups = {};\n    const pairs = [];\n\n    free.forEach(function(t){\n      if(!groups[t.symbol]) groups[t.symbol] = [];\n      groups[t.symbol].push(t);\n    });\n\n    Object.keys(groups).forEach(function(k){\n      const g = groups[k];\n      if(g.length >= 2){\n        for(let i = 0; i < g.length - 1; i++){\n          pairs.push([g[i],g[i + 1]]);\n        }\n      }\n    });\n\n    return pairs;\n  }\n\n  function hasValidMove(){\n    return findValidPairs().length > 0;\n  }\n\n  function canvasPoint(ev){\n    const rect = canvas.getBoundingClientRect();\n    const px = ev.clientX - rect.left;\n    const py = ev.clientY - rect.top;\n\n    if(((__hbtAndValue) => __hbtAndValue ? (document.fullscreenElement === shell) : __hbtAndValue)(shell.classList.contains(\"clm01-portrait-full\"))){\n      return {\n        x:(1 - py \/ rect.height) * W,\n        y:(px \/ rect.width) * H\n      };\n    }\n\n    return {\n      x:px * W \/ rect.width,\n      y:py * H \/ rect.height\n    };\n  }\n\n  function pointHitsTileRect(x,y,t){\n    return ((__hbtAndValue) => __hbtAndValue ? (y <= t.sy + t.h) : __hbtAndValue)(((__hbtAndValue) => __hbtAndValue ? (y >= t.sy) : __hbtAndValue)(((__hbtAndValue) => __hbtAndValue ? (x <= t.sx + t.w) : __hbtAndValue)(x >= t.sx)));\n  }\n\n  function tileAt(x,y){\n    computeTileRects();\n\n    const topMost = state.tiles.slice().sort(function(a,b){\n      if(a.z !== b.z) return b.z - a.z;\n      if(a.y !== b.y) return b.y - a.y;\n      return b.x - a.x;\n    });\n\n    for(let i = 0; i < topMost.length; i++){\n      const t = topMost[i];\n      if(t.removed) continue;\n      if(pointHitsTileRect(x,y,t)) return t;\n    }\n\n    return null;\n  }\n\n  function clearSelection(){\n    state.tiles.forEach(function(t){t.selected = false;});\n    state.selectedId = null;\n  }\n\n  function freeMap(){\n    const map = {};\n\n    state.tiles.forEach(function(t){\n      if(((__hbtAndValue) => __hbtAndValue ? (isFree(t)) : __hbtAndValue)(isActive(t))){\n        map[t.id] = true;\n      }\n    });\n\n    return map;\n  }\n\n  function addMatchEffects(a,b,gain){\n    [a,b].forEach(function(t){\n      const cx = t.sx + t.w \/ 2;\n      const cy = t.sy + t.h \/ 2;\n\n      for(let i = 0; i < 12; i++){\n        const colorPick = Math.random();\n        state.particles.push({\n          x:cx,\n          y:cy,\n          vx:(Math.random() - .5) * 72,\n          vy:-24 - Math.random() * 70,\n          life:.55 + Math.random() * .25,\n          max:.8,\n          size:3 + Math.random() * 4,\n          color:colorPick > .72 ? \"#D9953F\" : colorPick > .40 ? \"#F4C542\" : \"#FFF8DF\"\n        });\n      }\n    });\n\n    state.floats.push({\n      text:\"+\" + gain + (state.combo >= 2 ? \"  COMBO x\" + state.combo : \"\"),\n      x:(a.sx + b.sx) \/ 2 + a.w \/ 2,\n      y:(a.sy + b.sy) \/ 2,\n      vy:-38,\n      life:1,\n      max:1,\n      color:\"#5A321F\"\n    });\n  }\n\n\n  function addUnlockSpark(t){\n    const cx = t.sx + t.w \/ 2;\n    const cy = t.sy + t.h \/ 2;\n\n    for(let i = 0; i < 5; i++){\n      state.particles.push({\n        x:cx + (Math.random() - .5) * t.w * .5,\n        y:cy + (Math.random() - .5) * t.h * .5,\n        vx:(Math.random() - .5) * 28,\n        vy:-20 - Math.random() * 35,\n        life:.55,\n        max:.55,\n        size:2 + Math.random() * 3,\n        color:Math.random() > .45 ? \"#F4C542\" : \"#FFF8DF\"\n      });\n    }\n  }\n\n\n  function matchTiles(a,b){\n    const before = freeMap();\n    clearSelection();\n\n    a.removing = 1;\n    b.removing = 1;\n\n    state.combo = Math.min(9,state.comboTimer > 0 ? state.combo + 1 : 1);\n    state.comboTimer = 2.4;\n\n    const gain = 20 + state.level + state.combo * 5;\n    state.score += gain;\n    state.moves += 1;\n    state.timeLeft = Math.min(state.timeLimit + 60,state.timeLeft + 1 + (state.combo >= 2 ? 2 : 0));\n\n    if(state.score > state.best){\n      state.best = state.score;\n      storageSet(KEY_BEST,String(state.best));\n    }\n\n    addMatchEffects(a,b,gain);\n\n    const after = freeMap();\n    state.tiles.forEach(function(t){\n      if(((__hbtAndValue) => __hbtAndValue ? (!before[t.id]) : __hbtAndValue)(after[t.id])){\n        t.unlockGlow = 1;\n        addUnlockSpark(t);\n      }\n    });\n\n    sound(\"match\",state.combo);\n    updateHud();\n\n    if(activeTiles().length === 0){\n      state.pendingClear = true;\n    }else{\n      setTimeout(function(){\n        if(((__hbtAndValue) => __hbtAndValue ? (!hasValidMove()) : __hbtAndValue)(((__hbtAndValue) => __hbtAndValue ? (!hasRemovingTiles()) : __hbtAndValue)(((__hbtAndValue) => __hbtAndValue ? (!state.pendingClear) : __hbtAndValue)(state.mode === \"playing\")))){\n          if(canOfferShuffle()) showNoMovesPopup();\n          else showGameOverPopup();\n        }\n      },420);\n    }\n\n    saveProgress();\n  }\n\n  function useHint(){\n    if(state.mode !== \"playing\" || state.levelIntro > 0 || hasRemovingTiles()) return;\n\n    const pairs = findValidPairs();\n\n    if(!pairs.length){\n      if(canOfferShuffle()) showNoMovesPopup();\n      else showGameOverPopup();\n      return;\n    }\n\n    clearSelection();\n\n    const pair = pairs[Math.floor(Math.random() * pairs.length)];\n    state.hintIds = [pair[0].id,pair[1].id];\n    state.hintTimer = 1.8;\n    state.usedHint = true;\n    state.score = Math.max(0,state.score - 30);\n    state.timeLeft = Math.max(0,state.timeLeft - 8);\n\n    sound(\"hint\");\n    updateHud();\n    saveProgress();\n  }\n\n  function shuffleTiles(fromNoMove){\n    if(state.mode !== \"playing\" || hasRemovingTiles()) return;\n\n    if(!canOfferShuffle()){\n      showGameOverPopup();\n      return;\n    }\n\n    const rng = seeded(Date.now() + state.level * 77 + Math.floor(Math.random() * 999999));\n    clearSelection();\n\n    let success = assignSolvableSymbols(rng,state.level);\n\n    if(!success){\n      success = relayoutRemainingTiles(rng);\n    }\n\n    state.usedShuffle = true;\n\n    if(!fromNoMove){\n      state.score = Math.max(0,state.score - 80);\n      state.timeLeft = Math.max(0,state.timeLeft - 15);\n    }\n\n    state.hintIds = [];\n    state.hintTimer = 0;\n\n    activeTiles().forEach(function(t){\n      t.unlockGlow = Math.max(t.unlockGlow,.55);\n    });\n\n    for(let i = 0; i < 42; i++){\n      state.particles.push({\n        x:W \/ 2 + (Math.random() - .5) * 560,\n        y:H \/ 2 + (Math.random() - .5) * 300,\n        vx:(Math.random() - .5) * 115,\n        vy:(Math.random() - .5) * 80,\n        life:.75,\n        max:.75,\n        size:2 + Math.random() * 4,\n        color:Math.random() > .66 ? \"#FFF8DF\" : Math.random() > .5 ? \"#F4C542\" : \"#D9953F\"\n      });\n    }\n\n    sound(\"shuffle\");\n    updateHud();\n    saveProgress();\n\n    if(!success || !hasValidMove()){\n      setTimeout(function(){\n        if(state.mode === \"playing\"){\n          if(canOfferShuffle()) showNoMovesPopup();\n          else showGameOverPopup();\n        }\n      },140);\n    }\n  }\n\n  function relayoutRemainingTiles(rng){\n    const active = activeTiles();\n\n    if(active.length < 2) return false;\n\n    const positions = createThreeLayerPositions(active.length,state.level,rng);\n\n    active.forEach(function(t,i){\n      t.x = positions[i].x;\n      t.y = positions[i].y;\n      t.z = positions[i].z;\n      t.selected = false;\n      t.deny = 0;\n      t.wrong = 0;\n      t.unlockGlow = Math.max(t.unlockGlow,.55);\n    });\n\n    state.bounds = makeBounds(state.tiles);\n    computeTileRects();\n\n    return ((__hbtAndValue) => __hbtAndValue ? (hasValidMove()) : __hbtAndValue)(assignSolvableSymbols(rng,state.level));\n  }\n\n  function completeLevelIfReady(){\n    if(!state.pendingClear) return;\n    if(hasRemovingTiles()) return;\n\n    state.pendingClear = false;\n\n    const clearBonus = state.level * 100;\n    const timeBonus = Math.ceil(state.timeLeft) * 2;\n    const noHint = state.usedHint ? 0 : 150;\n    const noShuffle = state.usedShuffle ? 0 : 200;\n    const perfect = (((__hbtAndValue) => __hbtAndValue ? (state.timeLeft > state.timeLimit * .28) : __hbtAndValue)(((__hbtAndValue) => __hbtAndValue ? (!state.usedShuffle) : __hbtAndValue)(!state.usedHint))) ? 300 : 0;\n\n    state.score += clearBonus + timeBonus + noHint + noShuffle + perfect;\n\n    if(state.score > state.best){\n      state.best = state.score;\n      storageSet(KEY_BEST,String(state.best));\n    }\n\n    updateHud();\n    sound(state.level >= 50 ? \"complete\" : \"clear\");\n    showLevelClearPopup();\n  }\n\n  function saveProgress(){\n    try{\n      if(!state.tiles.length) return;\n\n      const save = {\n        level:state.level,\n        score:state.score,\n        moves:state.moves,\n        timeLeft:state.timeLeft,\n        timeLimit:state.timeLimit,\n        usedHint:state.usedHint,\n        usedShuffle:state.usedShuffle,\n        bounds:state.bounds,\n        tiles:state.tiles.map(function(t){\n          return {\n            x:t.x,\n            y:t.y,\n            z:t.z,\n            symbol:t.symbol,\n            removed:!!t.removed || t.removing > 0\n          };\n        })\n      };\n\n      storageSet(KEY_SAVE,JSON.stringify(save));\n    }catch(e){}\n  }\n\n  function sound(type,combo){\n    if(state.muted) return;\n\n    try{\n      if(!audioCtx){\n        audioCtx = new (window.AudioContext || window.webkitAudioContext)();\n      }\n\n      const now = audioCtx.currentTime;\n      const gain = audioCtx.createGain();\n      gain.connect(audioCtx.destination);\n      gain.gain.setValueAtTime(.0001,now);\n\n      let freq = 420;\n      let dur = .08;\n      let vol = .048;\n      let wave = \"sine\";\n\n      if(type === \"button\"){freq = 330; dur = .060; vol = .030; wave = \"triangle\";}\n      if(type === \"tap\"){freq = 420; dur = .050; vol = .030; wave = \"triangle\";}\n      if(type === \"match\"){freq = 560 + Math.min(6,combo || 1) * 38; dur = .155; vol = .058;}\n      if(type === \"wrong\"){freq = 190; dur = .12; vol = .042; wave = \"triangle\";}\n      if(type === \"deny\"){freq = 155; dur = .085; vol = .034; wave = \"triangle\";}\n      if(type === \"hint\"){freq = 860; dur = .17; vol = .044;}\n      if(type === \"shuffle\"){freq = 360; dur = .22; vol = .046; wave = \"triangle\";}\n      if(type === \"clear\"){freq = 720; dur = .25; vol = .064;}\n      if(type === \"complete\"){freq = 920; dur = .34; vol = .072;}\n      if(type === \"tick\"){freq = 250; dur = .04; vol = .016;}\n\n      const osc = audioCtx.createOscillator();\n      osc.type = wave;\n      osc.frequency.setValueAtTime(freq,now);\n\n      gain.gain.exponentialRampToValueAtTime(vol,now + .012);\n      gain.gain.exponentialRampToValueAtTime(.0001,now + dur);\n\n      osc.connect(gain);\n      osc.start(now);\n      osc.stop(now + dur + .02);\n    }catch(e){}\n  }\n\n\n  function onCanvasDown(ev){\n    if(state.mode !== \"playing\") return;\n    if(state.levelIntro > 0) return;\n    if(hasRemovingTiles()) return;\n\n    const p = canvasPoint(ev);\n    const t = tileAt(p.x,p.y);\n\n    if(!t) return;\n\n    sound(\"tap\");\n\n    if(!isFree(t)){\n      t.deny = 1;\n      sound(\"deny\");\n      return;\n    }\n\n    if(state.selectedId === t.id){\n      t.selected = false;\n      state.selectedId = null;\n      return;\n    }\n\n    if(state.selectedId === null){\n      clearSelection();\n      t.selected = true;\n      state.selectedId = t.id;\n      return;\n    }\n\n    const first = state.tiles.find(function(tile){\n      return tile.id === state.selectedId;\n    });\n\n    if(!first || !isActive(first) || !isFree(first)){\n      clearSelection();\n      t.selected = true;\n      state.selectedId = t.id;\n      return;\n    }\n\n    if(((__hbtAndValue) => __hbtAndValue ? (isFree(t)) : __hbtAndValue)(((__hbtAndValue) => __hbtAndValue ? (isFree(first)) : __hbtAndValue)(first.symbol === t.symbol))){\n      matchTiles(first,t);\n    }else{\n      first.wrong = 1;\n      t.wrong = 1;\n      clearSelection();\n      sound(\"wrong\");\n    }\n  }\n\n  function update(dt){\n    if(state.mode === \"playing\"){\n      if(state.levelIntro > 0){\n        state.levelIntro = Math.max(0,state.levelIntro - dt);\n      }else{\n        state.timeLeft -= dt;\n\n        const sec = Math.ceil(state.timeLeft);\n\n        if(((__hbtAndValue) => __hbtAndValue ? (sec >= 0) : __hbtAndValue)(((__hbtAndValue) => __hbtAndValue ? (sec !== state.lastTickSecond) : __hbtAndValue)(state.timeLeft <= 30))){\n          state.lastTickSecond = sec;\n          sound(\"tick\");\n        }\n\n        if(state.timeLeft <= 0){\n          state.timeLeft = 0;\n          updateHud();\n          showTimeUpPopup();\n        }\n      }\n    }\n\n    if(state.comboTimer > 0){\n      state.comboTimer = Math.max(0,state.comboTimer - dt);\n      if(state.comboTimer <= 0) state.combo = 0;\n    }\n\n    if(state.hintTimer > 0){\n      state.hintTimer = Math.max(0,state.hintTimer - dt);\n      if(state.hintTimer <= 0) state.hintIds = [];\n    }\n\n    state.tiles.forEach(function(t){\n      if(t.removing > 0){\n        t.removing = Math.max(0,t.removing - dt * 3.2);\n        if(t.removing <= 0){\n          t.removed = true;\n          t.removing = 0;\n        }\n      }\n\n      if(t.deny > 0) t.deny = Math.max(0,t.deny - dt * 4.5);\n      if(t.wrong > 0) t.wrong = Math.max(0,t.wrong - dt * 4.5);\n      if(t.unlockGlow > 0) t.unlockGlow = Math.max(0,t.unlockGlow - dt * 1.8);\n    });\n\n    state.particles = state.particles.filter(function(p){\n      p.life -= dt;\n      p.x += p.vx * dt;\n      p.y += p.vy * dt;\n      p.vy += 40 * dt;\n      return p.life > 0;\n    });\n\n    state.floats = state.floats.filter(function(f){\n      f.life -= dt;\n      f.y += f.vy * dt;\n      return f.life > 0;\n    });\n\n    completeLevelIfReady();\n    updateHud();\n  }\n\n  function roundRect(c,x,y,w,h,r){\n    r = Math.min(r,w \/ 2,h \/ 2);\n    c.beginPath();\n    c.moveTo(x + r,y);\n    c.arcTo(x + w,y,x + w,y + h,r);\n    c.arcTo(x + w,y + h,x,y + h,r);\n    c.arcTo(x,y + h,x,y,r);\n    c.arcTo(x,y,x + w,y,r);\n    c.closePath();\n  }\n\n  function drawBackground(){\n    const g = ctx.createLinearGradient(0,0,0,H);\n    g.addColorStop(0,\"#2B1710\");\n    g.addColorStop(.32,\"#5A321F\");\n    g.addColorStop(.70,\"#9B6426\");\n    g.addColorStop(1,\"#FFF1C9\");\n    ctx.fillStyle = g;\n    ctx.fillRect(0,0,W,H);\n\n    ctx.save();\n\n    const lampGlow = ctx.createRadialGradient(1000,82,8,1000,82,240);\n    lampGlow.addColorStop(0,\"rgba(255,248,223,.88)\");\n    lampGlow.addColorStop(.38,\"rgba(244,197,66,.34)\");\n    lampGlow.addColorStop(1,\"rgba(244,197,66,0)\");\n    ctx.fillStyle = lampGlow;\n    ctx.beginPath();\n    ctx.arc(1000,82,240,0,Math.PI * 2);\n    ctx.fill();\n\n    function spine(x,y,w,h,color,lean){\n      ctx.save();\n      ctx.translate(x + w \/ 2,y + h \/ 2);\n      ctx.rotate(lean || 0);\n      roundRect(ctx,-w \/ 2,-h \/ 2,w,h,4);\n      ctx.fillStyle = color;\n      ctx.fill();\n      ctx.globalAlpha = .42;\n      ctx.strokeStyle = \"#FFF1C9\";\n      ctx.lineWidth = 1.1;\n      ctx.beginPath();\n      ctx.moveTo(-w * .22,-h * .36);\n      ctx.lineTo(-w * .22,h * .34);\n      ctx.moveTo(w * .22,-h * .28);\n      ctx.lineTo(w * .22,h * .26);\n      ctx.stroke();\n      ctx.restore();\n    }\n\n    const shelfColors = [\"#5A321F\",\"#6F4128\",\"#8F6431\",\"#B87333\",\"#C69032\",\"#3F2418\",\"#6F7F4F\"];\n    for(let row = 0; row < 4; row++){\n      const sy = 86 + row * 84;\n      ctx.globalAlpha = .34;\n      ctx.fillStyle = \"rgba(63,36,24,.70)\";\n      roundRect(ctx,38,sy + 52,356,10,4);\n      ctx.fill();\n      roundRect(ctx,886,sy + 52,356,10,4);\n      ctx.fill();\n      ctx.globalAlpha = .58;\n      for(let i = 0; i < 17; i++){\n        const bw = 11 + (i % 4) * 3;\n        const bh = 34 + ((i + row) % 6) * 5;\n        const lx = 46 + i * 20 + (row % 2) * 7;\n        const rx = 894 + i * 20 + (row % 2) * 7;\n        spine(lx,sy + 52 - bh,bw,bh,shelfColors[(i + row) % shelfColors.length],(i % 3 - 1) * .025);\n        spine(rx,sy + 52 - bh,bw,bh,shelfColors[(i + row + 2) % shelfColors.length],(1 - i % 3) * .022);\n      }\n    }\n    ctx.globalAlpha = 1;\n\n    \/\/ honeycomb window glow\n    ctx.save();\n    ctx.globalAlpha = .23;\n    roundRect(ctx,520,50,240,118,28);\n    ctx.fillStyle = \"rgba(255,248,223,.30)\";\n    ctx.fill();\n    ctx.strokeStyle = \"rgba(255,241,201,.36)\";\n    ctx.lineWidth = 2;\n    ctx.stroke();\n    for(let i = 0; i < 7; i++){\n      for(let j = 0; j < 3; j++){\n        const hx = 548 + i * 30 + (j % 2) * 15;\n        const hy = 76 + j * 26;\n        ctx.beginPath();\n        for(let k = 0; k < 6; k++){\n          const a = Math.PI \/ 6 + k * Math.PI \/ 3;\n          const px = hx + Math.cos(a) * 12;\n          const py = hy + Math.sin(a) * 12;\n          if(k === 0) ctx.moveTo(px,py); else ctx.lineTo(px,py);\n        }\n        ctx.closePath();\n        ctx.stroke();\n      }\n    }\n    ctx.restore();\n\n    \/\/ floating honey dust and tiny page flecks\n    for(let i = 0; i < 70; i++){\n      const x = (i * 137 + 49) % W;\n      const y = 34 + ((i * 71) % 470);\n      const r = 1.2 + (i % 5) * .55;\n      ctx.globalAlpha = .08 + (i % 4) * .035;\n      ctx.fillStyle = i % 3 === 0 ? \"#FFF8DF\" : (i % 3 === 1 ? \"#F4C542\" : \"#FFF1C9\");\n      if(i % 7 === 0){\n        ctx.save();\n        ctx.translate(x,y);\n        ctx.rotate(i * .42);\n        roundRect(ctx,-r * 2,-r,r * 4,r * 2,1.5);\n        ctx.fill();\n        ctx.restore();\n      }else{\n        ctx.beginPath();\n        ctx.arc(x,y,r,0,Math.PI * 2);\n        ctx.fill();\n      }\n    }\n    ctx.globalAlpha = 1;\n\n    \/\/ desk and foreground warmth\n    const desk = ctx.createLinearGradient(0,560,0,H);\n    desk.addColorStop(0,\"rgba(111,65,40,.88)\");\n    desk.addColorStop(.48,\"rgba(90,50,31,.96)\");\n    desk.addColorStop(1,\"rgba(63,36,24,1)\");\n    ctx.fillStyle = desk;\n    ctx.beginPath();\n    ctx.moveTo(0,566);\n    ctx.bezierCurveTo(160,538,310,580,470,550);\n    ctx.bezierCurveTo(650,518,820,580,1000,548);\n    ctx.bezierCurveTo(1120,528,1190,554,W,538);\n    ctx.lineTo(W,H);\n    ctx.lineTo(0,H);\n    ctx.closePath();\n    ctx.fill();\n\n    ctx.globalAlpha = .18;\n    ctx.strokeStyle = \"#FFF1C9\";\n    ctx.lineWidth = 1.4;\n    for(let k = 0; k < 6; k++){\n      ctx.beginPath();\n      ctx.moveTo(42,592 + k * 14);\n      ctx.quadraticCurveTo(610,558 + k * 7,1238,592 + k * 14);\n      ctx.stroke();\n    }\n    ctx.globalAlpha = 1;\n\n    \/\/ decorative open book and honey jar silhouettes\n    ctx.save();\n    ctx.globalAlpha = .25;\n    roundRect(ctx,82,594,96,42,12);\n    ctx.fillStyle = \"rgba(255,248,223,.52)\";\n    ctx.fill();\n    ctx.strokeStyle = \"rgba(63,36,24,.42)\";\n    ctx.stroke();\n    ctx.beginPath();\n    ctx.moveTo(130,598);\n    ctx.lineTo(130,634);\n    ctx.stroke();\n\n    roundRect(ctx,1104,584,60,70,14);\n    ctx.fillStyle = \"rgba(244,197,66,.42)\";\n    ctx.fill();\n    roundRect(ctx,1116,572,36,16,6);\n    ctx.fillStyle = \"rgba(255,241,201,.52)\";\n    ctx.fill();\n    ctx.restore();\n\n    ctx.restore();\n  }\n\n\n  function drawBoardPanel(){\n    ctx.save();\n\n    const x = 76;\n    const y = 92;\n    const w = 1128;\n    const h = 530;\n\n    ctx.fillStyle = \"rgba(30,17,11,.30)\";\n    roundRect(ctx,x + 16,y + 22,w - 22,h - 4,36);\n    ctx.fill();\n\n    const outer = ctx.createLinearGradient(0,y,0,y + h);\n    outer.addColorStop(0,\"rgba(255,248,223,.28)\");\n    outer.addColorStop(.40,\"rgba(184,115,51,.20)\");\n    outer.addColorStop(1,\"rgba(63,36,24,.26)\");\n    ctx.fillStyle = outer;\n    roundRect(ctx,x,y,w,h,34);\n    ctx.fill();\n\n    const inner = ctx.createRadialGradient(x + w \/ 2,y + h * .18,10,x + w \/ 2,y + h * .18,620);\n    inner.addColorStop(0,\"rgba(255,248,223,.20)\");\n    inner.addColorStop(.45,\"rgba(244,197,66,.08)\");\n    inner.addColorStop(1,\"rgba(63,36,24,.18)\");\n    ctx.fillStyle = inner;\n    roundRect(ctx,x + 10,y + 10,w - 20,h - 20,28);\n    ctx.fill();\n\n    ctx.strokeStyle = \"rgba(244,197,66,.42)\";\n    ctx.lineWidth = 2.2;\n    roundRect(ctx,x + 1.5,y + 1.5,w - 3,h - 3,33);\n    ctx.stroke();\n\n    ctx.strokeStyle = \"rgba(255,248,223,.34)\";\n    ctx.lineWidth = 1.4;\n    roundRect(ctx,x + 13,y + 13,w - 26,h - 26,25);\n    ctx.stroke();\n\n    \/\/ subtle archive grid texture\n    ctx.globalAlpha = .09;\n    ctx.strokeStyle = \"#FFF1C9\";\n    ctx.lineWidth = 1;\n    for(let i = 0; i < 18; i++){\n      const gx = x + 44 + i * 58;\n      ctx.beginPath();\n      ctx.moveTo(gx,y + 34);\n      ctx.lineTo(gx,y + h - 34);\n      ctx.stroke();\n    }\n    for(let j = 0; j < 7; j++){\n      const gy = y + 50 + j * 60;\n      ctx.beginPath();\n      ctx.moveTo(x + 34,gy);\n      ctx.lineTo(x + w - 34,gy);\n      ctx.stroke();\n    }\n    ctx.globalAlpha = 1;\n\n    ctx.restore();\n  }\n\n\n  function drawStar(x,y,outer,inner,points,color){\n    ctx.fillStyle = color;\n    ctx.beginPath();\n\n    for(let i = 0; i < points * 2; i++){\n      const r = i % 2 === 0 ? outer : inner;\n      const a = -Math.PI \/ 2 + i * Math.PI \/ points;\n      const px = x + Math.cos(a) * r;\n      const py = y + Math.sin(a) * r;\n\n      if(i === 0) ctx.moveTo(px,py);\n      else ctx.lineTo(px,py);\n    }\n\n    ctx.closePath();\n    ctx.fill();\n  }\n\n  function drawSymbol(symbol,x,y,s){\n    ctx.save();\n    ctx.lineCap = \"round\";\n    ctx.lineJoin = \"round\";\n\n    const cream = \"#FFF8DF\";\n    const parchment = \"#FFF1C9\";\n    const honey = \"#F4C542\";\n    const amber = \"#D9953F\";\n    const brown = \"#5A321F\";\n    const cocoa = \"#3F2418\";\n    const sage = \"#6F7F4F\";\n    const rose = \"#B8735A\";\n\n    if(symbol === \"book\"){\n      ctx.fillStyle = brown;\n      roundRect(ctx,x - s * .34,y - s * .44,s * .68,s * .88,s * .08);\n      ctx.fill();\n      ctx.fillStyle = \"rgba(255,241,201,.32)\";\n      roundRect(ctx,x - s * .24,y - s * .36,s * .14,s * .72,s * .03);\n      ctx.fill();\n      ctx.strokeStyle = honey;\n      ctx.lineWidth = s * .055;\n      roundRect(ctx,x - s * .34,y - s * .44,s * .68,s * .88,s * .08);\n      ctx.stroke();\n      ctx.strokeStyle = parchment;\n      ctx.lineWidth = s * .035;\n      ctx.beginPath();\n      ctx.moveTo(x - s * .02,y - s * .22);\n      ctx.lineTo(x + s * .20,y - s * .22);\n      ctx.moveTo(x - s * .02,y - s * .03);\n      ctx.lineTo(x + s * .18,y - s * .03);\n      ctx.moveTo(x - s * .02,y + s * .16);\n      ctx.lineTo(x + s * .16,y + s * .16);\n      ctx.stroke();\n    }\n\n    if(symbol === \"honeyjar\"){\n      const jar = ctx.createLinearGradient(x - s * .32,y - s * .30,x + s * .32,y + s * .38);\n      jar.addColorStop(0,\"#FFF8DF\");\n      jar.addColorStop(.38,\"#F4C542\");\n      jar.addColorStop(1,\"#B87333\");\n      ctx.fillStyle = jar;\n      roundRect(ctx,x - s * .33,y - s * .28,s * .66,s * .64,s * .13);\n      ctx.fill();\n      ctx.strokeStyle = brown;\n      ctx.lineWidth = s * .052;\n      ctx.stroke();\n      ctx.fillStyle = parchment;\n      roundRect(ctx,x - s * .21,y - s * .47,s * .42,s * .18,s * .06);\n      ctx.fill();\n      ctx.strokeStyle = brown;\n      ctx.stroke();\n      ctx.fillStyle = \"rgba(255,248,223,.70)\";\n      roundRect(ctx,x - s * .21,y - s * .03,s * .42,s * .18,s * .06);\n      ctx.fill();\n      ctx.fillStyle = amber;\n      ctx.beginPath();\n      ctx.arc(x - s * .10,y - s * .13,s * .045,0,Math.PI * 2);\n      ctx.arc(x + s * .14,y + s * .20,s * .040,0,Math.PI * 2);\n      ctx.fill();\n    }\n\n    if(symbol === \"teacup\"){\n      ctx.fillStyle = cream;\n      ctx.beginPath();\n      ctx.moveTo(x - s * .34,y - s * .12);\n      ctx.quadraticCurveTo(x - s * .28,y + s * .34,x + s * .18,y + s * .28);\n      ctx.quadraticCurveTo(x + s * .34,y + s * .10,x + s * .30,y - s * .12);\n      ctx.closePath();\n      ctx.fill();\n      ctx.strokeStyle = brown;\n      ctx.lineWidth = s * .052;\n      ctx.stroke();\n      ctx.beginPath();\n      ctx.arc(x + s * .33,y + s * .04,s * .13,-Math.PI \/ 2,Math.PI \/ 2);\n      ctx.stroke();\n      ctx.strokeStyle = honey;\n      ctx.lineWidth = s * .04;\n      ctx.beginPath();\n      ctx.moveTo(x - s * .18,y - s * .34);\n      ctx.quadraticCurveTo(x - s * .08,y - s * .49,x,y - s * .32);\n      ctx.moveTo(x + s * .08,y - s * .34);\n      ctx.quadraticCurveTo(x + s * .18,y - s * .49,x + s * .25,y - s * .32);\n      ctx.stroke();\n      ctx.strokeStyle = amber;\n      ctx.beginPath();\n      ctx.moveTo(x - s * .36,y + s * .38);\n      ctx.lineTo(x + s * .38,y + s * .38);\n      ctx.stroke();\n    }\n\n    if(symbol === \"parchment\"){\n      ctx.fillStyle = parchment;\n      roundRect(ctx,x - s * .40,y - s * .35,s * .80,s * .70,s * .10);\n      ctx.fill();\n      ctx.strokeStyle = brown;\n      ctx.lineWidth = s * .045;\n      ctx.stroke();\n      ctx.fillStyle = \"rgba(184,115,51,.22)\";\n      roundRect(ctx,x - s * .35,y - s * .30,s * .18,s * .60,s * .07);\n      ctx.fill();\n      ctx.strokeStyle = amber;\n      ctx.lineWidth = s * .035;\n      for(let i = 0; i < 4; i++){\n        ctx.beginPath();\n        ctx.moveTo(x - s * .08,y - s * .20 + i * s * .12);\n        ctx.lineTo(x + s * .27,y - s * .20 + i * s * .12);\n        ctx.stroke();\n      }\n    }\n\n    if(symbol === \"bookmark\"){\n      ctx.fillStyle = honey;\n      ctx.beginPath();\n      ctx.moveTo(x - s * .18,y - s * .48);\n      ctx.lineTo(x + s * .18,y - s * .48);\n      ctx.lineTo(x + s * .18,y + s * .44);\n      ctx.lineTo(x,y + s * .25);\n      ctx.lineTo(x - s * .18,y + s * .44);\n      ctx.closePath();\n      ctx.fill();\n      ctx.strokeStyle = brown;\n      ctx.lineWidth = s * .052;\n      ctx.stroke();\n      ctx.strokeStyle = cream;\n      ctx.lineWidth = s * .04;\n      ctx.beginPath();\n      ctx.moveTo(x - s * .08,y - s * .18);\n      ctx.lineTo(x + s * .08,y - s * .18);\n      ctx.moveTo(x - s * .08,y + s * .02);\n      ctx.lineTo(x + s * .08,y + s * .02);\n      ctx.stroke();\n    }\n\n    if(symbol === \"glasses\"){\n      ctx.strokeStyle = brown;\n      ctx.lineWidth = s * .07;\n      ctx.beginPath();\n      ctx.arc(x - s * .18,y + s * .02,s * .18,0,Math.PI * 2);\n      ctx.arc(x + s * .18,y + s * .02,s * .18,0,Math.PI * 2);\n      ctx.stroke();\n      ctx.beginPath();\n      ctx.moveTo(x - s * .01,y + s * .02);\n      ctx.lineTo(x + s * .01,y + s * .02);\n      ctx.moveTo(x - s * .35,y - s * .02);\n      ctx.lineTo(x - s * .48,y - s * .14);\n      ctx.moveTo(x + s * .35,y - s * .02);\n      ctx.lineTo(x + s * .48,y - s * .14);\n      ctx.stroke();\n      ctx.strokeStyle = honey;\n      ctx.lineWidth = s * .03;\n      ctx.beginPath();\n      ctx.arc(x - s * .18,y + s * .02,s * .11,0,Math.PI * 2);\n      ctx.arc(x + s * .18,y + s * .02,s * .11,0,Math.PI * 2);\n      ctx.stroke();\n    }\n\n    if(symbol === \"key\"){\n      ctx.strokeStyle = honey;\n      ctx.lineWidth = s * .085;\n      ctx.beginPath();\n      ctx.arc(x - s * .20,y - s * .08,s * .16,0,Math.PI * 2);\n      ctx.moveTo(x - s * .04,y - s * .08);\n      ctx.lineTo(x + s * .42,y + s * .28);\n      ctx.moveTo(x + s * .22,y + s * .12);\n      ctx.lineTo(x + s * .10,y + s * .26);\n      ctx.moveTo(x + s * .32,y + s * .20);\n      ctx.lineTo(x + s * .22,y + s * .34);\n      ctx.stroke();\n      ctx.strokeStyle = brown;\n      ctx.lineWidth = s * .035;\n      ctx.stroke();\n    }\n\n    if(symbol === \"bookstack\"){\n      const colors = [sage,amber,brown];\n      for(let i = 0; i < 3; i++){\n        ctx.fillStyle = colors[i];\n        roundRect(ctx,x - s * .42 + i * s * .05,y - s * .30 + i * s * .24,s * .78,s * .20,s * .05);\n        ctx.fill();\n        ctx.strokeStyle = cream;\n        ctx.lineWidth = s * .028;\n        ctx.beginPath();\n        ctx.moveTo(x - s * .25 + i * s * .05,y - s * .20 + i * s * .24);\n        ctx.lineTo(x + s * .22 + i * s * .05,y - s * .20 + i * s * .24);\n        ctx.stroke();\n      }\n    }\n\n    if(symbol === \"quill\"){\n      ctx.fillStyle = cream;\n      ctx.beginPath();\n      ctx.moveTo(x + s * .32,y - s * .46);\n      ctx.bezierCurveTo(x - s * .20,y - s * .38,x - s * .38,y + s * .04,x - s * .08,y + s * .22);\n      ctx.bezierCurveTo(x + s * .06,y + s * .02,x + s * .18,y - s * .22,x + s * .32,y - s * .46);\n      ctx.fill();\n      ctx.strokeStyle = brown;\n      ctx.lineWidth = s * .04;\n      ctx.stroke();\n      ctx.beginPath();\n      ctx.moveTo(x + s * .24,y - s * .36);\n      ctx.lineTo(x - s * .28,y + s * .44);\n      ctx.stroke();\n      ctx.strokeStyle = honey;\n      ctx.beginPath();\n      ctx.moveTo(x - s * .12,y - s * .10);\n      ctx.lineTo(x + s * .13,y - s * .19);\n      ctx.moveTo(x - s * .20,y + s * .04);\n      ctx.lineTo(x + s * .05,y - s * .03);\n      ctx.stroke();\n    }\n\n    if(symbol === \"honeydrop\"){\n      ctx.fillStyle = honey;\n      ctx.beginPath();\n      ctx.moveTo(x,y - s * .48);\n      ctx.quadraticCurveTo(x + s * .38,y - s * .02,x + s * .20,y + s * .30);\n      ctx.quadraticCurveTo(x,y + s * .58,x - s * .20,y + s * .30);\n      ctx.quadraticCurveTo(x - s * .38,y - s * .02,x,y - s * .48);\n      ctx.fill();\n      ctx.strokeStyle = brown;\n      ctx.lineWidth = s * .05;\n      ctx.stroke();\n      ctx.fillStyle = \"rgba(255,248,223,.55)\";\n      ctx.beginPath();\n      ctx.ellipse(x - s * .10,y - s * .08,s * .07,s * .16,-.4,0,Math.PI * 2);\n      ctx.fill();\n    }\n\n    if(symbol === \"ink\"){\n      ctx.fillStyle = cocoa;\n      roundRect(ctx,x - s * .28,y - s * .22,s * .56,s * .58,s * .09);\n      ctx.fill();\n      ctx.strokeStyle = honey;\n      ctx.lineWidth = s * .045;\n      ctx.stroke();\n      ctx.fillStyle = amber;\n      roundRect(ctx,x - s * .16,y - s * .42,s * .32,s * .18,s * .05);\n      ctx.fill();\n      ctx.fillStyle = cream;\n      roundRect(ctx,x - s * .18,y - s * .02,s * .36,s * .16,s * .05);\n      ctx.fill();\n      ctx.fillStyle = honey;\n      ctx.beginPath();\n      ctx.arc(x,y + s * .06,s * .035,0,Math.PI * 2);\n      ctx.fill();\n    }\n\n    if(symbol === \"lamp\"){\n      ctx.fillStyle = honey;\n      ctx.beginPath();\n      ctx.moveTo(x - s * .32,y - s * .14);\n      ctx.lineTo(x + s * .32,y - s * .14);\n      ctx.lineTo(x + s * .18,y + s * .10);\n      ctx.lineTo(x - s * .18,y + s * .10);\n      ctx.closePath();\n      ctx.fill();\n      ctx.strokeStyle = brown;\n      ctx.lineWidth = s * .05;\n      ctx.stroke();\n      ctx.beginPath();\n      ctx.moveTo(x,y + s * .10);\n      ctx.lineTo(x,y + s * .42);\n      ctx.moveTo(x - s * .28,y + s * .42);\n      ctx.lineTo(x + s * .28,y + s * .42);\n      ctx.stroke();\n      ctx.fillStyle = \"rgba(255,248,223,.40)\";\n      ctx.beginPath();\n      ctx.ellipse(x,y + s * .03,s * .34,s * .22,0,0,Math.PI * 2);\n      ctx.fill();\n    }\n\n    if(symbol === \"pageswirl\"){\n      ctx.strokeStyle = amber;\n      ctx.lineWidth = s * .075;\n      ctx.beginPath();\n      for(let a = 0; a < Math.PI * 2.55; a += .08){\n        const r = s * .06 + a * s * .055;\n        const px = x + Math.cos(a) * r;\n        const py = y + Math.sin(a) * r;\n        if(a === 0) ctx.moveTo(px,py); else ctx.lineTo(px,py);\n      }\n      ctx.stroke();\n      ctx.strokeStyle = cream;\n      ctx.lineWidth = s * .038;\n      ctx.beginPath();\n      ctx.arc(x,y,s * .44,0,Math.PI * 2);\n      ctx.stroke();\n    }\n\n    if(symbol === \"bee\"){\n      ctx.save();\n      ctx.rotate(-.12);\n      ctx.fillStyle = honey;\n      ctx.beginPath();\n      ctx.ellipse(x,y,s * .30,s * .22,0,0,Math.PI * 2);\n      ctx.fill();\n      ctx.strokeStyle = brown;\n      ctx.lineWidth = s * .045;\n      ctx.stroke();\n      ctx.beginPath();\n      ctx.moveTo(x - s * .12,y - s * .20);\n      ctx.lineTo(x - s * .12,y + s * .20);\n      ctx.moveTo(x + s * .04,y - s * .20);\n      ctx.lineTo(x + s * .04,y + s * .20);\n      ctx.stroke();\n      ctx.fillStyle = \"rgba(255,248,223,.70)\";\n      ctx.beginPath();\n      ctx.ellipse(x - s * .10,y - s * .25,s * .15,s * .09,-.4,0,Math.PI * 2);\n      ctx.ellipse(x + s * .10,y - s * .25,s * .15,s * .09,.4,0,Math.PI * 2);\n      ctx.fill();\n      ctx.fillStyle = brown;\n      ctx.beginPath();\n      ctx.arc(x + s * .30,y - s * .02,s * .06,0,Math.PI * 2);\n      ctx.fill();\n      ctx.restore();\n    }\n\n    if(symbol === \"waxribbon\"){\n      ctx.fillStyle = rose;\n      ctx.beginPath();\n      ctx.moveTo(x,y);\n      ctx.lineTo(x - s * .46,y - s * .28);\n      ctx.lineTo(x - s * .40,y + s * .30);\n      ctx.closePath();\n      ctx.fill();\n      ctx.beginPath();\n      ctx.moveTo(x,y);\n      ctx.lineTo(x + s * .46,y - s * .28);\n      ctx.lineTo(x + s * .40,y + s * .30);\n      ctx.closePath();\n      ctx.fill();\n      ctx.fillStyle = amber;\n      ctx.beginPath();\n      ctx.arc(x,y,s * .18,0,Math.PI * 2);\n      ctx.fill();\n      ctx.strokeStyle = cream;\n      ctx.lineWidth = s * .03;\n      ctx.beginPath();\n      ctx.arc(x,y,s * .10,0,Math.PI * 2);\n      ctx.stroke();\n    }\n\n    if(symbol === \"openbook\"){\n      ctx.fillStyle = parchment;\n      ctx.beginPath();\n      ctx.moveTo(x,y - s * .30);\n      ctx.quadraticCurveTo(x - s * .26,y - s * .42,x - s * .46,y - s * .24);\n      ctx.lineTo(x - s * .42,y + s * .34);\n      ctx.quadraticCurveTo(x - s * .18,y + s * .22,x,y + s * .34);\n      ctx.closePath();\n      ctx.fill();\n      ctx.beginPath();\n      ctx.moveTo(x,y - s * .30);\n      ctx.quadraticCurveTo(x + s * .26,y - s * .42,x + s * .46,y - s * .24);\n      ctx.lineTo(x + s * .42,y + s * .34);\n      ctx.quadraticCurveTo(x + s * .18,y + s * .22,x,y + s * .34);\n      ctx.closePath();\n      ctx.fill();\n      ctx.strokeStyle = brown;\n      ctx.lineWidth = s * .04;\n      ctx.stroke();\n      ctx.beginPath();\n      ctx.moveTo(x,y - s * .30);\n      ctx.lineTo(x,y + s * .34);\n      ctx.stroke();\n      ctx.strokeStyle = amber;\n      ctx.lineWidth = s * .027;\n      for(let i = 0; i < 3; i++){\n        ctx.beginPath();\n        ctx.moveTo(x - s * .34,y - s * .14 + i * s * .13);\n        ctx.lineTo(x - s * .10,y - s * .08 + i * s * .10);\n        ctx.moveTo(x + s * .10,y - s * .08 + i * s * .10);\n        ctx.lineTo(x + s * .34,y - s * .14 + i * s * .13);\n        ctx.stroke();\n      }\n    }\n\n    if(symbol === \"honeycomb\"){\n      ctx.strokeStyle = amber;\n      ctx.lineWidth = s * .052;\n      ctx.fillStyle = \"rgba(244,197,66,.20)\";\n      function hex(cx2,cy2,r){\n        ctx.beginPath();\n        for(let i = 0; i < 6; i++){\n          const a = Math.PI \/ 6 + i * Math.PI \/ 3;\n          const px = cx2 + Math.cos(a) * r;\n          const py = cy2 + Math.sin(a) * r;\n          if(i === 0) ctx.moveTo(px,py); else ctx.lineTo(px,py);\n        }\n        ctx.closePath();\n        ctx.fill();\n        ctx.stroke();\n      }\n      hex(x,y,s * .15);\n      hex(x - s * .20,y,s * .14);\n      hex(x + s * .20,y,s * .14);\n      hex(x - s * .10,y - s * .18,s * .13);\n      hex(x + s * .10,y + s * .18,s * .13);\n    }\n\n    if(symbol === \"candle\"){\n      ctx.fillStyle = parchment;\n      roundRect(ctx,x - s * .15,y - s * .05,s * .30,s * .48,s * .06);\n      ctx.fill();\n      ctx.strokeStyle = brown;\n      ctx.lineWidth = s * .04;\n      ctx.stroke();\n      const flame = ctx.createRadialGradient(x,y - s * .24,1,x,y - s * .24,s * .22);\n      flame.addColorStop(0,\"#FFF8DF\");\n      flame.addColorStop(.45,\"#F4C542\");\n      flame.addColorStop(1,\"rgba(244,197,66,0)\");\n      ctx.fillStyle = flame;\n      ctx.beginPath();\n      ctx.moveTo(x,y - s * .52);\n      ctx.quadraticCurveTo(x + s * .18,y - s * .30,x,y - s * .16);\n      ctx.quadraticCurveTo(x - s * .16,y - s * .30,x,y - s * .52);\n      ctx.fill();\n      ctx.strokeStyle = amber;\n      ctx.lineWidth = s * .04;\n      ctx.beginPath();\n      ctx.moveTo(x - s * .28,y + s * .43);\n      ctx.lineTo(x + s * .28,y + s * .43);\n      ctx.stroke();\n    }\n\n    ctx.restore();\n  }\n\n\n  function drawTile(t){\n    if(t.removed) return;\n\n    const alpha = t.removing > 0 ? clamp(t.removing,0,1) : 1;\n    const cx = t.sx + t.w \/ 2;\n    const cy = t.sy + t.h \/ 2;\n    const scale = t.removing > 0 ? .82 + alpha * .18 : 1;\n    const wiggle = t.deny > 0 ? Math.sin(t.deny * 28) * 5 : 0;\n\n    ctx.save();\n    ctx.globalAlpha = alpha;\n    ctx.translate(cx + wiggle,cy);\n    ctx.scale(scale,scale);\n    ctx.translate(-cx,-cy);\n\n    const free = isFree(t);\n    const selected = t.selected;\n    const hint = ((__hbtAndValue) => __hbtAndValue ? (state.hintTimer > 0) : __hbtAndValue)(state.hintIds.indexOf(t.id) !== -1);\n    const glow = Math.max(t.unlockGlow,hint ? .7 : 0,selected ? 1 : 0);\n\n    if(glow > 0){\n      ctx.save();\n      ctx.globalAlpha = .20 + glow * .25;\n      ctx.fillStyle = selected ? \"#F4C542\" : \"#FFF1C9\";\n      roundRect(ctx,t.sx - 9,t.sy - 9,t.w + 18,t.h + 18,18);\n      ctx.fill();\n      ctx.restore();\n    }\n\n    if(t.wrong > 0 || t.deny > 0){\n      ctx.save();\n      ctx.globalAlpha = Math.max(t.wrong,t.deny) * .28;\n      ctx.fillStyle = \"#C2412D\";\n      roundRect(ctx,t.sx - 7,t.sy - 7,t.w + 14,t.h + 14,16);\n      ctx.fill();\n      ctx.restore();\n    }\n\n    ctx.save();\n    ctx.fillStyle = \"rgba(63,36,24,.25)\";\n    roundRect(ctx,t.sx + t.w * .10,t.sy + t.h * .11,t.w,t.h,15);\n    ctx.fill();\n    ctx.restore();\n\n    ctx.save();\n    ctx.fillStyle = free ? \"#B87333\" : \"#7A6A5D\";\n    roundRect(ctx,t.sx + t.w * .075,t.sy + t.h * .075,t.w,t.h,15);\n    ctx.fill();\n    ctx.fillStyle = free ? \"rgba(90,50,31,.13)\" : \"rgba(63,36,24,.30)\";\n    roundRect(ctx,t.sx + t.w * .075,t.sy + t.h * .075,t.w,t.h,15);\n    ctx.fill();\n    ctx.restore();\n\n    const bodyGrad = ctx.createLinearGradient(0,t.sy,0,t.sy + t.h);\n    if(free){\n      bodyGrad.addColorStop(0,\"#FFF8DF\");\n      bodyGrad.addColorStop(.58,\"#FFF1C9\");\n      bodyGrad.addColorStop(1,\"#D9B06F\");\n    }else{\n      bodyGrad.addColorStop(0,\"#CBBCA5\");\n      bodyGrad.addColorStop(.60,\"#A99579\");\n      bodyGrad.addColorStop(1,\"#84715E\");\n    }\n\n    ctx.fillStyle = bodyGrad;\n    roundRect(ctx,t.sx,t.sy,t.w,t.h,15);\n    ctx.fill();\n\n    ctx.save();\n    ctx.globalAlpha = free ? .45 : .22;\n    ctx.fillStyle = \"#FFF8DF\";\n    roundRect(ctx,t.sx + t.w * .10,t.sy + t.h * .08,t.w * .80,t.h * .13,8);\n    ctx.fill();\n    ctx.restore();\n\n    ctx.strokeStyle = selected ? \"#F4C542\" : (free ? \"#5A321F\" : \"#6B5A4A\");\n    ctx.lineWidth = selected ? 4 : 3;\n    roundRect(ctx,t.sx + 1.5,t.sy + 1.5,t.w - 3,t.h - 3,14);\n    ctx.stroke();\n\n    ctx.strokeStyle = free ? \"#D9953F\" : \"#8F806F\";\n    ctx.lineWidth = 1.5;\n    roundRect(ctx,t.sx + 6,t.sy + 7,t.w - 12,t.h - 14,9);\n    ctx.stroke();\n\n    ctx.save();\n    ctx.globalAlpha = free ? .42 : .22;\n    ctx.strokeStyle = \"#5A321F\";\n    ctx.lineWidth = 1;\n    ctx.setLineDash([3,5]);\n    roundRect(ctx,t.sx + 10,t.sy + 11,t.w - 20,t.h - 22,7);\n    ctx.stroke();\n    ctx.setLineDash([]);\n    ctx.restore();\n\n    ctx.save();\n    ctx.globalAlpha = free ? 1 : .60;\n    drawSymbol(t.symbol,t.sx + t.w \/ 2,t.sy + t.h \/ 2,Math.min(t.w,t.h) * .58);\n    ctx.restore();\n\n    ctx.restore();\n  }\n\n\n  function drawParticles(){\n    state.particles.forEach(function(p){\n      const a = clamp(p.life \/ p.max,0,1);\n      ctx.save();\n      ctx.globalAlpha = a;\n      ctx.fillStyle = p.color;\n      ctx.beginPath();\n      ctx.arc(p.x,p.y,p.size * (.4 + a * .8),0,Math.PI * 2);\n      ctx.fill();\n      ctx.restore();\n    });\n  }\n\n  function drawFloats(){\n    state.floats.forEach(function(f){\n      const a = clamp(f.life \/ f.max,0,1);\n      ctx.save();\n      ctx.globalAlpha = a;\n      ctx.fillStyle = f.color;\n      ctx.font = \"900 24px Arial, Helvetica, sans-serif\";\n      ctx.textAlign = \"center\";\n      ctx.textBaseline = \"middle\";\n      ctx.shadowColor = \"rgba(10,12,30,.34)\";\n      ctx.shadowBlur = 8;\n      ctx.fillText(f.text,f.x,f.y);\n      ctx.restore();\n    });\n  }\n\n  function drawLevelIntro(){\n    const a = Math.min(1,state.levelIntro);\n\n    ctx.save();\n    ctx.globalAlpha = clamp(a,0,1) * .92;\n    ctx.fillStyle = \"rgba(142,201,230,.24)\";\n    ctx.fillRect(0,0,W,H);\n\n    ctx.textAlign = \"center\";\n    ctx.textBaseline = \"middle\";\n    ctx.fillStyle = \"#fff4dc\";\n    ctx.font = \"900 42px Arial, Helvetica, sans-serif\";\n    ctx.shadowColor = \"rgba(72,106,134,.24)\";\n    ctx.shadowBlur = 12;\n    ctx.fillText(\"LEVEL \" + state.level,W \/ 2,H \/ 2 - 24);\n\n    ctx.font = \"800 25px Arial, Helvetica, sans-serif\";\n    ctx.fillStyle = \"rgba(72,106,134,.86)\";\n    ctx.shadowBlur = 0;\n    ctx.fillText(\"MATCH OPEN TILES ONLY\",W \/ 2,H \/ 2 + 24);\n    ctx.restore();\n  }\n\n\n  function draw(){\n    computeTileRects();\n    drawBackground();\n    drawBoardPanel();\n\n    const drawList = state.tiles.slice().sort(function(a,b){\n      if(a.z !== b.z) return a.z - b.z;\n      if(a.y !== b.y) return a.y - b.y;\n      return a.x - b.x;\n    });\n\n    drawList.forEach(drawTile);\n    drawParticles();\n    drawFloats();\n\n    if(((__hbtAndValue) => __hbtAndValue ? (state.mode === \"playing\") : __hbtAndValue)(state.levelIntro > 0)){\n      drawLevelIntro();\n    }\n  }\n\n  let hbtVisible = true;\n  if (typeof IntersectionObserver !== \"undefined\") {\n    const hbtVisibilityObserver = new IntersectionObserver(function(entries){\n      hbtVisible = entries[0].isIntersecting;\n    },{threshold:0.01});\n    hbtVisibilityObserver.observe(shell);\n  }\n\n  function loop(now){\n    const dt = Math.min(.05,(now - lastTime) \/ 1000);\n    lastTime = now;\n    if(document.hidden || !hbtVisible){\n      requestAnimationFrame(loop);\n      return;\n    }\n\n    update(dt);\n    draw();\n\n    requestAnimationFrame(loop);\n  }\n\n  soundBtn.addEventListener(\"click\",function(){\n    state.muted = !state.muted;\n    storageSet(KEY_MUTE,state.muted ? \"1\" : \"0\");\n    sound(\"button\");\n    updateHud();\n  });\n\n  pauseBtn.addEventListener(\"click\",function(){\n    sound(\"button\");\n    if(state.mode === \"playing\") showPausePopup();\n    else if(state.mode === \"paused\"){\n      hideOverlay();\n      state.mode = \"playing\";\n    }\n  });\n\n  fullBtn.addEventListener(\"click\",function(){\n    sound(\"button\");\n    if(document.fullscreenElement === shell){\n      document.exitFullscreen().catch(function(){});\n      unlockOrientation();\n    }else{\n      shell.requestFullscreen().then(function(){\n        tryOrientationLock();\n        updateFullscreenClass();\n      }).catch(function(){});\n    }\n  });\n\n  hintBtn.addEventListener(\"click\",function(){\n    sound(\"button\");\n    useHint();\n  });\n\n  shuffleBtn.addEventListener(\"click\",function(){\n    sound(\"button\");\n    if(state.mode === \"playing\") shuffleTiles(false);\n  });\n\n  canvas.addEventListener(\"pointerdown\",onCanvasDown);\n\n  document.addEventListener(\"fullscreenchange\",updateFullscreenClass);\n  window.addEventListener(\"resize\",updateFullscreenClass);\n  window.addEventListener(\"orientationchange\",function(){\n    setTimeout(updateFullscreenClass,250);\n  });\n\n  window.addEventListener(\"beforeunload\",saveProgress);\n\n  let hbtKeyboardActive = false;\n  shell.addEventListener(\"pointerdown\",function(){hbtKeyboardActive = true;});\n  shell.addEventListener(\"focusin\",function(){hbtKeyboardActive = true;});\n  document.addEventListener(\"pointerdown\",function(ev){\n    if(!shell.contains(ev.target)) hbtKeyboardActive = false;\n  });\n  document.addEventListener(\"keydown\",function(ev){\n    if(!hbtKeyboardActive){\n      if(document.fullscreenElement !== shell) return;\n    }\n    const target = ev.target;\n    if(target){\n      if(target.closest){\n        if(target.closest('input,textarea,select,[contenteditable=\"true\"]')) return;\n      }\n    }\n    if(ev.key === \"Escape\"){\n      if(state.mode === \"playing\") showPausePopup();\n    }\n    if(ev.key === \"p\" || ev.key === \"P\"){\n      if(state.mode === \"playing\") showPausePopup();\n    }\n    if(ev.key === \"m\" || ev.key === \"M\"){\n      state.muted = !state.muted;\n      storageSet(KEY_MUTE,state.muted ? \"1\" : \"0\");\n      updateHud();\n    }\n  });\n\n  resizeCanvas();\n  updateHud();\n  showStartPopup();\n  requestAnimationFrame(loop);\n  }\n\n  if(document.readyState === \"loading\"){\n    document.addEventListener(\"DOMContentLoaded\",initializeHoneyboundTiles,{once:true});\n  }else{\n    initializeHoneyboundTiles();\n  }\n})();<\/script>\n\n\n\n<h2 class=\"wp-block-heading\">Match Open Tiles and Clear the Golden Shelf<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The gameplay is easy to understand but rewarding to master. Tap one open tile, then tap another matching open tile. If both are free, the pair disappears in a gentle honey glow, giving you more space and revealing new possible matches.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">As you progress, the layouts become larger and more complex. The game includes 50 levels, a timer, score tracking, best score saving, move counter, hint button, shuffle button, pause mode, sound toggle, fullscreen support, and save-and-continue progress.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Why You Will Love Honeybound Tiles<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Honeybound Tiles<\/strong> is perfect for players who enjoy calm puzzle games with a thoughtful pace. It is relaxing enough for a short break, but still challenging enough to make every move feel important.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Step into the golden reading room, follow the honeylight across the stacked tiles, and see how many shelves you can clear before the library lamp fades.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Honeybound Tiles is a cozy Mahjong Solitaire puzzle game set inside a warm honeylit library, where every tile feels like a tiny piece of an old golden archive. Books, honey jars, teacups, bookmarks, reading glasses, keys, quills, candles, bees, and honeycomb pages are stacked across the board, waiting to be uncovered and matched. Your goal &hellip;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[12],"tags":[],"class_list":["post-58","post","type-post","status-publish","format-standard","hentry","category-puzzle"],"_links":{"self":[{"href":"https:\/\/play.produsenpagarbrc.com\/index.php?rest_route=\/wp\/v2\/posts\/58","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/play.produsenpagarbrc.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/play.produsenpagarbrc.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/play.produsenpagarbrc.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/play.produsenpagarbrc.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=58"}],"version-history":[{"count":0,"href":"https:\/\/play.produsenpagarbrc.com\/index.php?rest_route=\/wp\/v2\/posts\/58\/revisions"}],"wp:attachment":[{"href":"https:\/\/play.produsenpagarbrc.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=58"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/play.produsenpagarbrc.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=58"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/play.produsenpagarbrc.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=58"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}