{"id":110,"date":"2026-10-11T00:11:15","date_gmt":"2026-10-11T00:11:15","guid":{"rendered":"https:\/\/apk.produsenpagarbrc.com\/?p=110"},"modified":"2026-10-11T00:11:15","modified_gmt":"2026-10-11T00:11:15","slug":"mochi-moonfall","status":"publish","type":"post","link":"https:\/\/play.produsenpagarbrc.com\/?p=110","title":{"rendered":"Mochi Moonfall"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\"><strong>Mochi Moonfall<\/strong> is a cozy falling-block puzzle game set inside a dreamy moon rabbit workshop above the night clouds. Under soft creamy moonlight, tiny workshop pieces begin to fall: mochi cakes, wooden mallets, moon lanterns, soft clouds, little toolboxes, rabbit charms, and glowing lunar treasures.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Your goal is to guide each falling piece into the best position, stack them carefully, and clear full rows before the board fills up. Every move feels like helping the moon rabbits organize their magical workbench after a long night of crafting rice cakes and wooden tools beneath the stars.<\/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\/Mochi-Moonfall-1024x768.png\" alt=\"\" class=\"wp-image-111\"\/><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">A Soft Falling-Block Puzzle With Moonlit Charm<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Mochi Moonfall<\/strong> keeps the classic joy of falling-block gameplay while wrapping it in a gentle fantasy world. You can move pieces left or right, rotate them, drop them slowly, or send them down quickly when you already know where they belong.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The challenge grows as you clear more lines. The pieces fall faster, the board becomes tighter, and every decision starts to matter. But even when the game becomes more intense, the soft moon colors, creamy glow, and cozy workshop atmosphere keep the experience calm and comfortable.<\/p>\n\n\n\n<style>\n  #mochiMoonfallGame,\n  #mochiMoonfallGame * {\n    box-sizing: border-box;\n    user-select: none;\n    -webkit-tap-highlight-color: transparent;\n  }\n\n  #mochiMoonfallGame {\n    width: 100%;\n    padding: 10px 0;\n    display: flex;\n    justify-content: center;\n    align-items: center;\n    overflow: visible;\n    font-family: system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif;\n  }\n\n  #mochiMoonfallGame .mmf-stage {\n    position: relative;\n    width: min(96vw, 430px);\n    aspect-ratio: 9 \/ 16;\n    overflow: hidden;\n    border-radius: 28px;\n    background:\n      radial-gradient(circle at 78% 8%, rgba(255, 241, 201, .64), transparent 22%),\n      radial-gradient(circle at 22% 17%, rgba(175, 151, 224, .28), transparent 32%),\n      radial-gradient(circle at 70% 86%, rgba(103, 69, 127, .32), transparent 34%),\n      linear-gradient(180deg, #17142B 0%, #30224B 42%, #5A426C 73%, #FFF1C9 100%);\n    box-shadow:\n      0 22px 58px rgba(30, 22, 56, .34),\n      inset 0 0 0 1px rgba(255, 241, 201, .42);\n  }\n\n  #mochiMoonfallGame canvas {\n    width: 100%;\n    height: 100%;\n    display: block;\n    touch-action: none;\n  }\n\n  #mochiMoonfallGame .mmf-topbar {\n    position: absolute;\n    top: calc(14px + env(safe-area-inset-top));\n    left: calc(14px + env(safe-area-inset-left));\n    right: calc(14px + env(safe-area-inset-right));\n    z-index: 45;\n    display: flex;\n    justify-content: space-between;\n    align-items: center;\n    pointer-events: none;\n  }\n\n  #mochiMoonfallGame .mmf-top-left,\n  #mochiMoonfallGame .mmf-top-right {\n    display: flex;\n    gap: 9px;\n    pointer-events: auto;\n  }\n\n  #mochiMoonfallGame .mmf-icon-btn,\n  #mochiMoonfallGame .mmf-control-btn {\n    border: 1px solid rgba(255, 241, 201, .34);\n    color: #FFF1C9;\n    display: inline-flex;\n    justify-content: center;\n    align-items: center;\n    cursor: pointer;\n    padding: 0;\n    backdrop-filter: blur(10px);\n    -webkit-backdrop-filter: blur(10px);\n    transition: transform .12s ease, opacity .14s ease, visibility .14s ease, background .14s ease, border-color .14s ease;\n  }\n\n  #mochiMoonfallGame .mmf-icon-btn {\n    width: 38px;\n    height: 38px;\n    border-radius: 15px;\n    background:\n      radial-gradient(circle at 32% 22%, rgba(255, 241, 201, .28), transparent 35%),\n      linear-gradient(180deg, rgba(85, 61, 112, .88), rgba(31, 24, 58, .78));\n    box-shadow:\n      0 9px 21px rgba(20, 14, 42, .28),\n      0 0 18px rgba(255, 241, 201, .14),\n      inset 0 1px 0 rgba(255, 241, 201, .22),\n      inset 0 -3px 6px rgba(18, 13, 35, .18);\n  }\n\n  #mochiMoonfallGame .mmf-icon-btn:active,\n  #mochiMoonfallGame .mmf-control-btn:active { transform: scale(.94); }\n\n  #mochiMoonfallGame .mmf-icon-btn:hover,\n  #mochiMoonfallGame .mmf-control-btn:hover {\n    border-color: rgba(255, 241, 201, .62);\n    background:\n      radial-gradient(circle at 32% 22%, rgba(255, 241, 201, .34), transparent 35%),\n      linear-gradient(180deg, rgba(98, 73, 130, .94), rgba(38, 29, 67, .86));\n  }\n\n  #mochiMoonfallGame .mmf-css-icon {\n    position: relative;\n    width: 26px;\n    height: 26px;\n    display: block;\n  }\n\n  #mochiMoonfallGame .mmf-pause-icon { width: 24px; height: 24px; }\n  #mochiMoonfallGame .mmf-pause-icon::before,\n  #mochiMoonfallGame .mmf-pause-icon::after {\n    content: \"\";\n    position: absolute;\n    top: 5px;\n    width: 4px;\n    height: 14px;\n    border-radius: 999px;\n    background: currentColor;\n  }\n  #mochiMoonfallGame .mmf-pause-icon::before { left: 7px; }\n  #mochiMoonfallGame .mmf-pause-icon::after { right: 7px; }\n\n  #mochiMoonfallGame .mmf-pause.is-paused .mmf-pause-icon::before {\n    top: 5px;\n    left: 8px;\n    width: 0;\n    height: 0;\n    border-top: 7px solid transparent;\n    border-bottom: 7px solid transparent;\n    border-left: 11px solid currentColor;\n    border-radius: 0;\n    background: transparent;\n  }\n  #mochiMoonfallGame .mmf-pause.is-paused .mmf-pause-icon::after { display: none; }\n\n  #mochiMoonfallGame .mmf-sound-icon { width: 22px; height: 22px; transform: translateX(-1px); }\n  #mochiMoonfallGame .mmf-speaker-back {\n    position: absolute;\n    left: 0;\n    top: 7px;\n    width: 5px;\n    height: 9px;\n    background: currentColor;\n    border-radius: 3px 0 0 3px;\n  }\n  #mochiMoonfallGame .mmf-speaker-cone {\n    position: absolute;\n    left: 4px;\n    top: 3px;\n    width: 10px;\n    height: 17px;\n    background: currentColor;\n    clip-path: polygon(0 36%, 100% 0, 100% 100%, 0 64%);\n    border-radius: 2px;\n  }\n  #mochiMoonfallGame .mmf-wave {\n    position: absolute;\n    border-style: solid;\n    border-color: currentColor;\n    border-left-color: transparent;\n    border-top-color: transparent;\n    border-bottom-color: transparent;\n    border-radius: 0 999px 999px 0;\n    transition: opacity .14s ease;\n  }\n  #mochiMoonfallGame .mmf-wave-one { right: 5px; top: 8px; width: 4px; height: 7px; border-width: 2px; }\n  #mochiMoonfallGame .mmf-wave-two { right: 1px; top: 6px; width: 8px; height: 11px; border-width: 2px; }\n  #mochiMoonfallGame .mmf-wave-three { right: -3px; top: 3px; width: 12px; height: 17px; border-width: 2px; }\n  #mochiMoonfallGame .mmf-mute-slash {\n    position: absolute;\n    left: 1px;\n    top: 10px;\n    width: 21px;\n    height: 3px;\n    border-radius: 999px;\n    background: #E65C61;\n    transform: rotate(-42deg) scaleX(0);\n    transform-origin: center;\n    transition: transform .14s ease;\n  }\n  #mochiMoonfallGame .mmf-sound.is-muted .mmf-wave { opacity: 0; }\n  #mochiMoonfallGame .mmf-sound.is-muted .mmf-mute-slash { transform: rotate(-42deg) scaleX(1); }\n\n  #mochiMoonfallGame .mmf-full-icon { width: 24px; height: 24px; }\n  #mochiMoonfallGame .mmf-full-icon::before {\n    content: \"\";\n    position: absolute;\n    inset: 2px;\n    background:\n      linear-gradient(currentColor, currentColor) left top \/ 10px 3px no-repeat,\n      linear-gradient(currentColor, currentColor) left top \/ 3px 10px no-repeat,\n      linear-gradient(currentColor, currentColor) right top \/ 10px 3px no-repeat,\n      linear-gradient(currentColor, currentColor) right top \/ 3px 10px no-repeat,\n      linear-gradient(currentColor, currentColor) left bottom \/ 10px 3px no-repeat,\n      linear-gradient(currentColor, currentColor) left bottom \/ 3px 10px no-repeat,\n      linear-gradient(currentColor, currentColor) right bottom \/ 10px 3px no-repeat,\n      linear-gradient(currentColor, currentColor) right bottom \/ 3px 10px no-repeat;\n  }\n\n  #mochiMoonfallGame .mmf-controls {\n    position: absolute;\n    left: calc(14px + env(safe-area-inset-left));\n    right: calc(14px + env(safe-area-inset-right));\n    bottom: calc(14px + env(safe-area-inset-bottom));\n    z-index: 35;\n    display: flex;\n    justify-content: space-between;\n    align-items: center;\n    gap: 10px;\n    padding: 8px 10px;\n    border-radius: 24px;\n    background: rgba(32, 25, 57, .54);\n    border: 1px solid rgba(255, 241, 201, .26);\n    backdrop-filter: blur(10px);\n    -webkit-backdrop-filter: blur(10px);\n    box-shadow:\n      0 12px 28px rgba(20, 14, 42, .30),\n      inset 0 1px 0 rgba(255, 241, 201, .16);\n    transition: opacity .14s ease, visibility .14s ease;\n  }\n\n  #mochiMoonfallGame .mmf-control-btn {\n    width: 58px;\n    height: 58px;\n    flex: 0 0 58px;\n    border-radius: 18px;\n    color: #FFF1C9;\n    background:\n      radial-gradient(circle at 30% 24%, rgba(255, 241, 201, .36), transparent 32%),\n      linear-gradient(180deg, #7F61A8, #30224B);\n    box-shadow:\n      0 10px 22px rgba(20, 14, 42, .30),\n      inset 0 1px 0 rgba(255, 241, 201, .34),\n      inset 0 -3px 6px rgba(18, 13, 35, .20);\n  }\n  #mochiMoonfallGame .mmf-control-btn.mmf-rotate {\n    background:\n      radial-gradient(circle at 30% 24%, rgba(255, 241, 201, .40), transparent 32%),\n      linear-gradient(180deg, #E8C579, #7F61A8);\n  }\n  #mochiMoonfallGame .mmf-control-btn.mmf-down {\n    background:\n      radial-gradient(circle at 30% 24%, rgba(255, 241, 201, .35), transparent 32%),\n      linear-gradient(180deg, #B9A6DE, #5B437E);\n  }\n  #mochiMoonfallGame .mmf-control-btn.mmf-hard {\n    background:\n      radial-gradient(circle at 30% 24%, rgba(255, 241, 201, .38), transparent 32%),\n      linear-gradient(180deg, #DCA66F, #8B5A3B);\n  }\n\n  #mochiMoonfallGame .mmf-arrow-left::before,\n  #mochiMoonfallGame .mmf-arrow-right::before,\n  #mochiMoonfallGame .mmf-arrow-down::before {\n    content: \"\";\n    position: absolute;\n    left: 3px;\n    top: 3px;\n    width: 20px;\n    height: 20px;\n    background: currentColor;\n    clip-path: polygon(28% 0, 72% 0, 72% 42%, 100% 42%, 50% 100%, 0 42%, 28% 42%);\n    transform-origin: center;\n  }\n  #mochiMoonfallGame .mmf-arrow-left::before { transform: rotate(90deg); }\n  #mochiMoonfallGame .mmf-arrow-right::before { transform: rotate(-90deg); }\n  #mochiMoonfallGame .mmf-arrow-down::before { transform: rotate(0deg); }\n\n  #mochiMoonfallGame .mmf-hard-icon::before,\n  #mochiMoonfallGame .mmf-hard-icon::after {\n    content: \"\";\n    position: absolute;\n    left: 3px;\n    width: 20px;\n    height: 10px;\n    background: currentColor;\n    clip-path: polygon(0 0, 24% 0, 50% 58%, 76% 0, 100% 0, 50% 100%);\n  }\n  #mochiMoonfallGame .mmf-hard-icon::before { top: 3px; }\n  #mochiMoonfallGame .mmf-hard-icon::after { top: 12px; }\n\n  #mochiMoonfallGame .mmf-rotate-icon { width: 24px; height: 24px; }\n  #mochiMoonfallGame .mmf-rotate-icon .mmf-rot-arc {\n    position: absolute;\n    width: 15px;\n    height: 15px;\n    border: 4px solid currentColor;\n    border-radius: 50%;\n  }\n  #mochiMoonfallGame .mmf-rotate-icon .mmf-rot-arc.a { left: 0; top: 0; border-right-color: transparent; border-bottom-color: transparent; transform: rotate(16deg); }\n  #mochiMoonfallGame .mmf-rotate-icon .mmf-rot-arc.b { right: 0; bottom: 0; border-left-color: transparent; border-top-color: transparent; transform: rotate(16deg); }\n  #mochiMoonfallGame .mmf-rotate-icon .mmf-rot-head { position: absolute; width: 0; height: 0; }\n  #mochiMoonfallGame .mmf-rotate-icon .mmf-rot-head.a { right: 0; top: 1px; border-left: 8px solid currentColor; border-top: 5px solid transparent; border-bottom: 5px solid transparent; transform: rotate(-8deg); }\n  #mochiMoonfallGame .mmf-rotate-icon .mmf-rot-head.b { left: 0; bottom: 1px; border-right: 8px solid currentColor; border-top: 5px solid transparent; border-bottom: 5px solid transparent; transform: rotate(-8deg); }\n\n  #mochiMoonfallGame .mmf-overlay {\n    position: absolute;\n    inset: 0;\n    z-index: 40;\n    display: flex;\n    justify-content: center;\n    align-items: center;\n    padding: 26px;\n    background:\n      radial-gradient(circle at 50% 24%, rgba(255, 241, 201, .30), transparent 35%),\n      radial-gradient(circle at 58% 64%, rgba(185, 166, 222, .22), transparent 34%),\n      rgba(20, 14, 42, .36);\n    backdrop-filter: blur(9px);\n    -webkit-backdrop-filter: blur(9px);\n    opacity: 1;\n    pointer-events: auto;\n    transition: opacity .18s ease;\n  }\n  #mochiMoonfallGame .mmf-overlay.mmf-hidden { opacity: 0; pointer-events: none; }\n\n  #mochiMoonfallGame .mmf-card {\n    width: min(100%, 315px);\n    padding: 23px 19px 19px;\n    border-radius: 28px;\n    text-align: center;\n    background:\n      radial-gradient(circle at 76% 8%, rgba(255, 241, 201, .38), transparent 26%),\n      radial-gradient(circle at 22% 20%, rgba(185, 166, 222, .30), transparent 31%),\n      linear-gradient(145deg, rgba(48, 34, 75, .96), rgba(32, 25, 57, .94));\n    border: 1px solid rgba(255, 241, 201, .36);\n    box-shadow:\n      0 24px 62px rgba(20, 14, 42, .42),\n      0 0 32px rgba(255, 241, 201, .18),\n      inset 0 0 0 1px rgba(255, 241, 201, .12);\n  }\n\n  #mochiMoonfallGame .mmf-mark {\n    width: 58px;\n    height: 58px;\n    margin: 0 auto 12px;\n    border-radius: 21px;\n    background:\n      radial-gradient(circle at 50% 50%, #FFF1C9 0 14px, transparent 15px),\n      radial-gradient(circle at 50% 50%, rgba(255, 241, 201, .86) 0 24px, rgba(127, 97, 168, .92) 25px 100%);\n    box-shadow:\n      0 0 22px rgba(255, 241, 201, .34),\n      0 12px 24px rgba(20, 14, 42, .26);\n    position: relative;\n  }\n  #mochiMoonfallGame .mmf-mark::before,\n  #mochiMoonfallGame .mmf-mark::after {\n    content: \"\";\n    position: absolute;\n    top: 11px;\n    width: 9px;\n    height: 20px;\n    border-radius: 999px 999px 6px 6px;\n    background: #FFF1C9;\n    box-shadow: inset 0 -2px 0 rgba(127, 97, 168, .18);\n  }\n  #mochiMoonfallGame .mmf-mark::before { left: 20px; transform: rotate(-12deg); }\n  #mochiMoonfallGame .mmf-mark::after { right: 20px; transform: rotate(12deg); }\n\n  #mochiMoonfallGame .mmf-title {\n    margin: 0 0 8px;\n    color: #FFF1C9;\n    font-weight: 950;\n    font-size: 24px;\n    line-height: 1.14;\n    letter-spacing: -.02em;\n    text-shadow: 0 3px 14px rgba(20, 14, 42, .30);\n  }\n  #mochiMoonfallGame .mmf-text {\n    margin: 0 0 16px;\n    color: rgba(255, 248, 223, .82);\n    font-weight: 750;\n    font-size: 13px;\n    line-height: 1.45;\n  }\n\n  #mochiMoonfallGame .mmf-main-btn,\n  #mochiMoonfallGame .mmf-sub-btn {\n    width: 100%;\n    border: 0;\n    border-radius: 18px;\n    padding: 13px 16px;\n    cursor: pointer;\n    transition: transform .12s ease, filter .12s ease;\n    font-family: inherit;\n  }\n  #mochiMoonfallGame .mmf-main-btn {\n    background: linear-gradient(135deg, #FFF1C9, #E8C579 52%, #B98C5B);\n    color: #30224B;\n    font-size: 15px;\n    font-weight: 950;\n    box-shadow: 0 12px 28px rgba(20, 14, 42, .30);\n  }\n  #mochiMoonfallGame .mmf-sub-btn {\n    margin-top: 10px;\n    background: rgba(255, 241, 201, .14);\n    color: #FFF1C9;\n    border: 1px solid rgba(255, 241, 201, .28);\n    font-size: 14px;\n    font-weight: 850;\n  }\n  #mochiMoonfallGame .mmf-main-btn:active,\n  #mochiMoonfallGame .mmf-sub-btn:active { transform: scale(.97); }\n  #mochiMoonfallGame .mmf-hidden { display: none; }\n\n  #mochiMoonfallGame.mmf-minimal-ui .mmf-controls,\n  #mochiMoonfallGame.mmf-minimal-ui .mmf-pause,\n  #mochiMoonfallGame.mmf-minimal-ui .mmf-sound {\n    opacity: 0;\n    visibility: hidden;\n    pointer-events: none;\n  }\n  #mochiMoonfallGame.mmf-minimal-ui .mmf-fullscreen {\n    opacity: 1;\n    visibility: visible;\n    pointer-events: auto;\n  }\n\n  #mochiMoonfallGame:fullscreen,\n  #mochiMoonfallGame:-webkit-full-screen {\n    width: 100vw;\n    height: 100vh;\n    padding: 0;\n    background: #17142B;\n  }\n  #mochiMoonfallGame:fullscreen .mmf-stage,\n  #mochiMoonfallGame:-webkit-full-screen .mmf-stage { border-radius: 0; }\n\n  @supports (height: 100dvh) {\n    #mochiMoonfallGame:fullscreen,\n    #mochiMoonfallGame:-webkit-full-screen {\n      width: 100dvw;\n      height: 100dvh;\n    }\n  }\n\n  @media (max-width: 390px) {\n    #mochiMoonfallGame .mmf-controls {\n      left: calc(10px + env(safe-area-inset-left));\n      right: calc(10px + env(safe-area-inset-right));\n      bottom: calc(12px + env(safe-area-inset-bottom));\n      gap: 7px;\n      padding: 7px 8px;\n      border-radius: 22px;\n    }\n    #mochiMoonfallGame .mmf-control-btn {\n      width: 52px;\n      height: 52px;\n      flex-basis: 52px;\n      border-radius: 17px;\n    }\n    #mochiMoonfallGame .mmf-arrow-left::before,\n    #mochiMoonfallGame .mmf-arrow-right::before,\n    #mochiMoonfallGame .mmf-arrow-down::before {\n      left: 4px;\n      top: 4px;\n      width: 18px;\n      height: 18px;\n    }\n    #mochiMoonfallGame .mmf-hard-icon::before,\n    #mochiMoonfallGame .mmf-hard-icon::after { left: 4px; width: 18px; }\n  }\n\n  @media (max-width: 350px) {\n    #mochiMoonfallGame .mmf-controls { gap: 6px; padding-left: 7px; padding-right: 7px; }\n    #mochiMoonfallGame .mmf-control-btn { width: 48px; height: 48px; flex-basis: 48px; }\n  }\n\n  \/* WordPress compatibility: safe fixed-position fullscreen on iOS and unsupported browsers. *\/\n  #mochiMoonfallGame.mmf-fallback-fullscreen {\n    position: fixed;\n    inset: 0;\n    z-index: 2147483647;\n    width: 100vw;\n    height: 100vh;\n    margin: 0;\n    padding: 0;\n    overflow: hidden;\n    background: #17142B;\n  }\n  #mochiMoonfallGame.mmf-fallback-fullscreen .mmf-stage {\n    border-radius: 0;\n  }\n  @supports (height: 100dvh) {\n    #mochiMoonfallGame.mmf-fallback-fullscreen {\n      width: 100dvw;\n      height: 100dvh;\n    }\n  }\n<\/style>\n<div id=\"mochiMoonfallGame\">\n  <div class=\"mmf-stage\">\n    <canvas class=\"mmf-canvas\" aria-label=\"Mochi Moonfall falling block puzzle\"><\/canvas>\n\n    <div class=\"mmf-topbar\">\n      <div class=\"mmf-top-left\">\n        <button class=\"mmf-icon-btn mmf-pause\" type=\"button\" aria-label=\"Pause\">\n          <span class=\"mmf-css-icon mmf-pause-icon\" aria-hidden=\"true\"><\/span>\n        <\/button>\n\n        <button class=\"mmf-icon-btn mmf-sound\" type=\"button\" aria-label=\"Sound\">\n          <span class=\"mmf-css-icon mmf-sound-icon\" aria-hidden=\"true\">\n            <span class=\"mmf-speaker-back\"><\/span>\n            <span class=\"mmf-speaker-cone\"><\/span>\n            <span class=\"mmf-wave mmf-wave-one\"><\/span>\n            <span class=\"mmf-wave mmf-wave-two\"><\/span>\n            <span class=\"mmf-wave mmf-wave-three\"><\/span>\n            <span class=\"mmf-mute-slash\"><\/span>\n          <\/span>\n        <\/button>\n      <\/div>\n\n      <div class=\"mmf-top-right\">\n        <button class=\"mmf-icon-btn mmf-fullscreen\" type=\"button\" aria-label=\"Fullscreen\">\n          <span class=\"mmf-css-icon mmf-full-icon\" aria-hidden=\"true\"><\/span>\n        <\/button>\n      <\/div>\n    <\/div>\n\n    <div class=\"mmf-controls\">\n      <button class=\"mmf-control-btn mmf-left\" type=\"button\" aria-label=\"Move left\">\n        <span class=\"mmf-css-icon mmf-arrow-left\" aria-hidden=\"true\"><\/span>\n      <\/button>\n      <button class=\"mmf-control-btn mmf-right\" type=\"button\" aria-label=\"Move right\">\n        <span class=\"mmf-css-icon mmf-arrow-right\" aria-hidden=\"true\"><\/span>\n      <\/button>\n      <button class=\"mmf-control-btn mmf-rotate\" type=\"button\" aria-label=\"Rotate\">\n        <span class=\"mmf-css-icon mmf-rotate-icon\" aria-hidden=\"true\">\n          <span class=\"mmf-rot-arc a\"><\/span>\n          <span class=\"mmf-rot-arc b\"><\/span>\n          <span class=\"mmf-rot-head a\"><\/span>\n          <span class=\"mmf-rot-head b\"><\/span>\n        <\/span>\n      <\/button>\n      <button class=\"mmf-control-btn mmf-down\" type=\"button\" aria-label=\"Soft drop\">\n        <span class=\"mmf-css-icon mmf-arrow-down\" aria-hidden=\"true\"><\/span>\n      <\/button>\n      <button class=\"mmf-control-btn mmf-hard\" type=\"button\" aria-label=\"Hard drop\">\n        <span class=\"mmf-css-icon mmf-hard-icon\" aria-hidden=\"true\"><\/span>\n      <\/button>\n    <\/div>\n\n    <div class=\"mmf-overlay\">\n      <div class=\"mmf-card\">\n        <div class=\"mmf-mark\" aria-hidden=\"true\"><\/div>\n        <p class=\"mmf-title\">Mochi Moonfall<\/p>\n        <p class=\"mmf-text\">Stack moonlit workshop pieces, clear glowing rows, and help the rabbits keep their mochi bench shining.<\/p>\n        <button class=\"mmf-main-btn\" type=\"button\">Start Moonfall<\/button>\n        <button class=\"mmf-sub-btn mmf-hidden\" type=\"button\">Reset the Workshop<\/button>\n      <\/div>\n    <\/div>\n  <\/div>\n<\/div>\n\n<script>\n(function () {\n  \"use strict\";\n\n  function initializeMochiMoonfall() {\n  var root = document.getElementById(\"mochiMoonfallGame\");\n  if (!root || root.dataset.mmfReady === \"1\") return;\n  var stage = root.querySelector(\".mmf-stage\");\n  var canvas = root.querySelector(\".mmf-canvas\");\n  if (!stage || !canvas) return;\n  var ctx = canvas.getContext(\"2d\");\n  if (!ctx) return;\n  root.dataset.mmfReady = \"1\";\n\n  var overlay = root.querySelector(\".mmf-overlay\");\n  var overlayTitle = root.querySelector(\".mmf-title\");\n  var overlayText = root.querySelector(\".mmf-text\");\n  var mainBtn = root.querySelector(\".mmf-main-btn\");\n  var subBtn = root.querySelector(\".mmf-sub-btn\");\n\n  var pauseBtn = root.querySelector(\".mmf-pause\");\n  var soundBtn = root.querySelector(\".mmf-sound\");\n  var fullscreenBtn = root.querySelector(\".mmf-fullscreen\");\n  var leftBtn = root.querySelector(\".mmf-left\");\n  var rightBtn = root.querySelector(\".mmf-right\");\n  var rotateBtn = root.querySelector(\".mmf-rotate\");\n  var downBtn = root.querySelector(\".mmf-down\");\n  var hardBtn = root.querySelector(\".mmf-hard\");\n\n  var W = 360;\n  var H = 640;\n  var COLS = 10;\n  var ROWS = 18;\n  var CELL = 25;\n  var BOARD_W = COLS * CELL;\n  var BOARD_H = ROWS * CELL;\n  var BOARD_X = Math.round((W - BOARD_W) \/ 2);\n  var BOARD_Y = 104;\n\n  var STORAGE_BEST = \"mochi_moonfall_best_v1\";\n  var STORAGE_MUTE = \"mochi_moonfall_mute_v1\";\n  var SFX_VOLUME = 1.18;\n\n  var board = [];\n  var current = null;\n  var nextPreview = null;\n  var bag = [];\n  var particles = [];\n  var ambientDust = [];\n  var clearWaves = [];\n  var impactGlows = [];\n  var score = 0;\n  var lines = 0;\n  var level = 1;\n  var best = safeNumber(storageGet(STORAGE_BEST), 0);\n  var started = false;\n  var paused = false;\n  var gameOver = false;\n  var overlayMode = \"start\";\n  var dropTimer = 0;\n  var lastTime = 0;\n  var muted = storageGet(STORAGE_MUTE) === \"1\";\n  var audioCtx = null;\n  var pointerStart = null;\n  var holds = {};\n  var keyboardActive = false;\n\n  function clearHolds() {\n    Object.keys(holds).forEach(function (key) {\n      var hold = holds[key];\n      if (hold) {\n        clearTimeout(hold.timeout);\n        clearInterval(hold.interval);\n      }\n      holds[key] = null;\n    });\n  }\n\n  var pieces = [\n    { matrix: [[1,1],[1,1]], color: \"#F3DFA8\", edge: \"#B98C5B\", icon: \"mochi\" },\n    { matrix: [[1,1,1,1]], color: \"#FFF1C9\", edge: \"#9C7AC8\", icon: \"mooncharm\" },\n    { matrix: [[0,1,0],[1,1,1]], color: \"#E8C579\", edge: \"#8B5A3B\", icon: \"lantern\" },\n    { matrix: [[1,0,0],[1,1,1]], color: \"#B98C5B\", edge: \"#6F4A35\", icon: \"mallet\" },\n    { matrix: [[0,0,1],[1,1,1]], color: \"#B9A6DE\", edge: \"#6B5594\", icon: \"cloud\" },\n    { matrix: [[0,1,1],[1,1,0]], color: \"#F2B8A2\", edge: \"#B86568\", icon: \"rabbit\" },\n    { matrix: [[1,1,0],[0,1,1]], color: \"#7F61A8\", edge: \"#3D2D64\", icon: \"toolbox\" }\n  ];\n\n  function storageGet(key) {\n    try { return localStorage.getItem(key); } catch (e) { return null; }\n  }\n\n  function storageSet(key, value) {\n    try { localStorage.setItem(key, value); } catch (e) {}\n  }\n\n  function safeNumber(value, fallback) {\n    var n = Number(value);\n    return Number.isFinite(n) ? n : fallback;\n  }\n\n  function syncUiMode() {\n    var minimal = paused || gameOver || overlayMode === \"start\" || overlayMode === \"restart\";\n    root.classList.toggle(\"mmf-minimal-ui\", minimal);\n  }\n\n  function fitStage() {\n    var isFull = document.fullscreenElement === root || document.webkitFullscreenElement === root || root.classList.contains(\"mmf-fallback-fullscreen\");\n    var rootRect = root.getBoundingClientRect();\n    var availableW = isFull ? window.innerWidth : Math.max(280, rootRect.width || window.innerWidth);\n    var availableH = isFull ? window.innerHeight : Math.min(window.innerHeight - 20, 760);\n\n    if (availableH < 360) availableH = Math.max(320, window.innerHeight - 12);\n\n    var maxW = isFull ? window.innerWidth : Math.min(availableW, 430);\n    var targetW = Math.min(maxW, availableH * 9 \/ 16);\n    var targetH = targetW * 16 \/ 9;\n\n    if (targetH > availableH) {\n      targetH = availableH;\n      targetW = targetH * 9 \/ 16;\n    }\n\n    stage.style.width = Math.floor(targetW) + \"px\";\n    stage.style.height = Math.floor(targetH) + \"px\";\n    resizeCanvas();\n  }\n\n  function resizeCanvas() {\n    var rect = canvas.getBoundingClientRect();\n    var dpr = Math.min(window.devicePixelRatio || 1, 2);\n    canvas.width = Math.max(1, Math.round(rect.width * dpr));\n    canvas.height = Math.max(1, Math.round(rect.height * dpr));\n    ctx.setTransform(canvas.width \/ W, 0, 0, canvas.height \/ H, 0, 0);\n    draw();\n  }\n\n  function createBoard() {\n    var rows = [];\n    for (var y = 0; y < ROWS; y++) rows.push(Array(COLS).fill(null));\n    return rows;\n  }\n\n  function clonePiece(piece) {\n    return {\n      matrix: piece.matrix.map(function (row) { return row.slice(); }),\n      color: piece.color,\n      edge: piece.edge,\n      icon: piece.icon,\n      x: 0,\n      y: 0\n    };\n  }\n\n  function shuffle(arr) {\n    for (var i = arr.length - 1; i > 0; i--) {\n      var j = Math.floor(Math.random() * (i + 1));\n      var temp = arr[i];\n      arr[i] = arr[j];\n      arr[j] = temp;\n    }\n    return arr;\n  }\n\n  function refillBag() { bag = shuffle(pieces.map(clonePiece)); }\n  function nextPiece() { if (!bag.length) refillBag(); return bag.pop(); }\n\n  function createAmbientDust() {\n    ambientDust = [];\n    for (var i = 0; i < 38; i++) {\n      var kindRand = Math.random();\n      var type = kindRand > .70 ? \"page\" : (kindRand > .36 ? \"glint\" : \"dust\");\n      var palette = type === \"page\"\n        ? [\"#FFF1C9\", \"#E9C875\", \"#D9953F\"]\n        : type === \"glint\"\n          ? [\"#FFF8DF\", \"#F4C542\", \"#FFE08A\"]\n          : [\"#FFF1C9\", \"#C69032\", \"#F4C542\"];\n      ambientDust.push({\n        type: type,\n        x: Math.random() * W,\n        y: Math.random() * H,\n        r: type === \"page\" ? 2.8 + Math.random() * 4.4 : .7 + Math.random() * 2.2,\n        speed: type === \"page\" ? .030 + Math.random() * .10 : .025 + Math.random() * .085,\n        alpha: type === \"page\" ? .10 + Math.random() * .18 : .07 + Math.random() * .18,\n        twinkle: Math.random() * Math.PI * 2,\n        drift: Math.random() * Math.PI * 2,\n        rot: Math.random() * Math.PI * 2,\n        spin: (Math.random() - .5) * .016,\n        color: palette[Math.floor(Math.random() * palette.length)]\n      });\n    }\n  }\n\n  function resetGame() {\n    clearHolds();\n    pointerStart = null;\n    board = createBoard();\n    particles = [];\n    clearWaves = [];\n    impactGlows = [];\n    score = 0;\n    lines = 0;\n    level = 1;\n    dropTimer = 0;\n    bag = [];\n    nextPreview = nextPiece();\n    started = true;\n    paused = false;\n    gameOver = false;\n    overlayMode = \"play\";\n    pauseBtn.classList.remove(\"is-paused\");\n    syncUiMode();\n    spawnPiece();\n    hideOverlay();\n  }\n\n  function spawnPiece() {\n    current = nextPreview || nextPiece();\n    nextPreview = nextPiece();\n    current.x = Math.floor((COLS - current.matrix[0].length) \/ 2);\n    current.y = -1;\n    if (collides(current.matrix, current.x, current.y)) endGame();\n  }\n\n  function endGame() {\n    clearHolds();\n    pointerStart = null;\n    gameOver = true;\n    started = false;\n    paused = false;\n    pauseBtn.classList.remove(\"is-paused\");\n\n    if (score > best) {\n      best = score;\n      storageSet(STORAGE_BEST, String(best));\n    }\n\n    showOverlay(\n      \"The Moon Bench Is Full\",\n      \"No more space remains on the moon rabbits\\' little workbench. Score \" + score + \" - Best \" + best + \".\",\n      \"Craft Again\",\n      false,\n      \"restart\"\n    );\n\n    sound(\"over\");\n  }\n\n  function showOverlay(title, text, mainLabel, showSub, mode) {\n    overlayMode = mode || \"start\";\n    overlayTitle.textContent = title;\n    overlayText.textContent = text;\n    mainBtn.textContent = mainLabel;\n    subBtn.classList.toggle(\"mmf-hidden\", !showSub);\n    overlay.classList.remove(\"mmf-hidden\");\n    syncUiMode();\n  }\n\n  function hideOverlay() {\n    overlay.classList.add(\"mmf-hidden\");\n    if (((__mmf_bool) => __mmf_bool ? (!gameOver) : __mmf_bool)(!paused)) overlayMode = \"play\";\n    syncUiMode();\n  }\n\n  function togglePause() {\n    if (!started || gameOver) return;\n    paused = !paused;\n    pauseBtn.classList.toggle(\"is-paused\", paused);\n\n    if (paused) {\n      clearHolds();\n      pointerStart = null;\n      showOverlay(\n        \"Workshop Pause\",\n        \"The moon rabbits are waiting softly. Take a breath, then return to the creamy moonlit stack.\",\n        \"Resume Crafting\",\n        true,\n        \"resume\"\n      );\n      subBtn.textContent = \"Reset the Workshop\";\n      sound(\"pause\");\n    } else {\n      hideOverlay();\n      sound(\"resume\");\n    }\n  }\n\n  function canPlay() { return ((__mmf_bool) => __mmf_bool ? (current) : __mmf_bool)(((__mmf_bool) => __mmf_bool ? (!gameOver) : __mmf_bool)(((__mmf_bool) => __mmf_bool ? (!paused) : __mmf_bool)(started))); }\n  function fallDelay() { return Math.max(108, 780 - (level - 1) * 52); }\n\n  function collides(matrix, px, py) {\n    for (var y = 0; y < matrix.length; y++) {\n      for (var x = 0; x < matrix[y].length; x++) {\n        if (!matrix[y][x]) continue;\n        var nx = px + x;\n        var ny = py + y;\n        if (nx < 0 || nx >= COLS || ny >= ROWS) return true;\n        if (((__mmf_bool) => __mmf_bool ? (board[ny][nx]) : __mmf_bool)(ny >= 0)) return true;\n      }\n    }\n    return false;\n  }\n\n  function move(dx) {\n    if (!canPlay()) return false;\n    if (!collides(current.matrix, current.x + dx, current.y)) {\n      current.x += dx;\n      sound(\"move\");\n      return true;\n    }\n    return false;\n  }\n\n  function stepDown() {\n    if (!canPlay()) return;\n    if (!collides(current.matrix, current.x, current.y + 1)) current.y++;\n    else mergePiece();\n  }\n\n  function softDrop(steps) {\n    if (!canPlay()) return;\n    var amount = Math.max(1, steps || 1);\n    for (var i = 0; i < amount; i++) {\n      if (!collides(current.matrix, current.x, current.y + 1)) {\n        current.y++;\n        score += 1;\n      } else {\n        mergePiece();\n        break;\n      }\n    }\n    saveBestIfNeeded();\n    dropTimer = 0;\n    sound(\"soft\");\n  }\n\n  function hardDrop() {\n    if (!canPlay()) return;\n    var distance = 0;\n    while (!collides(current.matrix, current.x, current.y + 1)) {\n      current.y++;\n      distance++;\n    }\n    score += distance * 2;\n    saveBestIfNeeded();\n    impactGlows.push({\n      x: BOARD_X + (current.x + current.matrix[0].length \/ 2) * CELL,\n      y: BOARD_Y + (current.y + current.matrix.length) * CELL,\n      age: 0,\n      life: 18,\n      color: current.color\n    });\n    sound(\"drop\");\n    mergePiece();\n    dropTimer = 0;\n  }\n\n  function saveBestIfNeeded() {\n    if (score > best) {\n      best = score;\n      storageSet(STORAGE_BEST, String(best));\n    }\n  }\n\n  function rotateMatrix(matrix) {\n    var h = matrix.length;\n    var w = matrix[0].length;\n    var result = [];\n    for (var y = 0; y < w; y++) result.push(Array(h).fill(0));\n    for (var yy = 0; yy < h; yy++) {\n      for (var xx = 0; xx < w; xx++) result[xx][h - 1 - yy] = matrix[yy][xx];\n    }\n    return trimMatrix(result);\n  }\n\n  function trimMatrix(matrix) {\n    var top = 0;\n    var bottom = matrix.length - 1;\n    var left = 0;\n    var right = matrix[0].length - 1;\n    while (((__mmf_bool) => __mmf_bool ? (matrix[top].every(function (v) { return !v; })) : __mmf_bool)(top <= bottom)) top++;\n    while (((__mmf_bool) => __mmf_bool ? (matrix[bottom].every(function (v) { return !v; })) : __mmf_bool)(bottom >= top)) bottom--;\n    while (((__mmf_bool) => __mmf_bool ? (matrix.every(function (row) { return !row[left]; })) : __mmf_bool)(left <= right)) left++;\n    while (((__mmf_bool) => __mmf_bool ? (matrix.every(function (row) { return !row[right]; })) : __mmf_bool)(right >= left)) right--;\n    var trimmed = [];\n    for (var y = top; y <= bottom; y++) trimmed.push(matrix[y].slice(left, right + 1));\n    return trimmed.length ? trimmed : [[1]];\n  }\n\n  function rotatePiece() {\n    if (!canPlay()) return false;\n    var rotated = rotateMatrix(current.matrix);\n    var kicks = [0, -1, 1, -2, 2];\n    for (var i = 0; i < kicks.length; i++) {\n      var nx = current.x + kicks[i];\n      if (!collides(rotated, nx, current.y)) {\n        current.matrix = rotated;\n        current.x = nx;\n        sound(\"rotate\");\n        return true;\n      }\n    }\n    return false;\n  }\n\n  function mergePiece() {\n    var lockedAboveTop = false;\n    current.matrix.forEach(function (row, y) {\n      row.forEach(function (value, x) {\n        if (!value) return;\n        var by = current.y + y;\n        var bx = current.x + x;\n        if (by < 0) lockedAboveTop = true;\n        if (((__mmf_bool) => __mmf_bool ? (bx < COLS) : __mmf_bool)(((__mmf_bool) => __mmf_bool ? (bx >= 0) : __mmf_bool)(((__mmf_bool) => __mmf_bool ? (by < ROWS) : __mmf_bool)(by >= 0)))) {\n          board[by][bx] = { color: current.color, edge: current.edge, icon: current.icon };\n        }\n      });\n    });\n    if (lockedAboveTop) {\n      endGame();\n      return;\n    }\n    sound(\"lock\");\n    clearLines();\n    spawnPiece();\n  }\n\n  function clearLines() {\n    var cleared = [];\n    for (var y = 0; y < ROWS; y++) {\n      var full = true;\n      for (var x = 0; x < COLS; x++) {\n        if (!board[y][x]) { full = false; break; }\n      }\n      if (full) cleared.push(y);\n    }\n    if (!cleared.length) return;\n\n    cleared.forEach(function (rowIndex) {\n      clearWaves.push({ y: BOARD_Y + rowIndex * CELL + CELL \/ 2, age: 0, life: 44 });\n      for (var x = 0; x < COLS; x++) {\n        var cell = board[rowIndex][x];\n        var cx = BOARD_X + x * CELL + CELL \/ 2;\n        var cy = BOARD_Y + rowIndex * CELL + CELL \/ 2;\n        burstTeaSpark(cx, cy, cell.color, cell.edge);\n      }\n    });\n\n    board = board.filter(function (row) { return !row.every(Boolean); });\n    while (board.length < ROWS) board.unshift(Array(COLS).fill(null));\n\n    var count = cleared.length;\n    var scores = [0, 100, 280, 520, 860];\n    score += (scores[count] || count * 250) * level;\n    lines += count;\n    level = 1 + Math.floor(lines \/ 7);\n    saveBestIfNeeded();\n    sound(count >= 3 ? \"bigClear\" : \"clear\");\n  }\n\n  function ghostY() {\n    if (!current) return 0;\n    var gy = current.y;\n    while (!collides(current.matrix, current.x, gy + 1)) gy++;\n    return gy;\n  }\n\n  function burstTeaSpark(x, y, color, edge) {\n    for (var i = 0; i < 5; i++) {\n      var angle = Math.PI * 2 * i \/ 5 + (Math.random() - .5) * .72;\n      var speed = .30 + Math.random() * .62;\n      particles.push({\n        type: \"spark\",\n        x: x + Math.cos(angle) * 2,\n        y: y + Math.sin(angle) * 2,\n        vx: Math.cos(angle) * speed,\n        vy: Math.sin(angle) * speed * .32 - .26,\n        age: 0,\n        life: 42 + Math.random() * 18,\n        r: 2.5 + Math.random() * 3.2,\n        rot: angle,\n        spin: (Math.random() - .5) * .06,\n        color: Math.random() > .42 ? \"#FFF8DF\" : color\n      });\n    }\n\n    for (var g = 0; g < 3; g++) {\n      var glintAngle = Math.random() * Math.PI * 2;\n      var glintSpeed = .24 + Math.random() * .58;\n      particles.push({\n        type: \"spark\",\n        x: x + (Math.random() - .5) * 8,\n        y: y + (Math.random() - .5) * 8,\n        vx: Math.cos(glintAngle) * glintSpeed,\n        vy: Math.sin(glintAngle) * glintSpeed * .42 - .38,\n        age: 0,\n        life: 28 + Math.random() * 18,\n        r: 2.8 + Math.random() * 2.2,\n        rot: glintAngle,\n        spin: (Math.random() - .5) * .12,\n        color: Math.random() > .35 ? \"#FFF1C9\" : \"#F4C542\"\n      });\n    }\n\n    if (Math.random() > .32) {\n      particles.push({\n        type: \"mist\",\n        x: x + (Math.random() - .5) * 8,\n        y: y + (Math.random() - .5) * 5,\n        vx: (Math.random() - .5) * .18,\n        vy: -.34 - Math.random() * .28,\n        age: 0,\n        life: 48 + Math.random() * 18,\n        r: 4.2 + Math.random() * 2.2,\n        rot: Math.random() * Math.PI,\n        spin: (Math.random() - .5) * .020,\n        color: Math.random() > .5 ? \"#FFF8DF\" : \"#FFE08A\"\n      });\n    }\n  }\n\n  function updateParticles() {\n    for (var i = particles.length - 1; i >= 0; i--) {\n      var p = particles[i];\n      p.age++;\n      p.x += p.vx;\n      p.y += p.vy;\n      p.vx *= .992;\n\n      if (p.type === \"mist\") {\n        p.vx += Math.sin((p.age + p.rot) * .09) * .006;\n        p.vy -= .002;\n      } else if (p.type === \"spark\") {\n        p.vy += .010;\n      } else if (p.type === \"bubble\") {\n        p.vx += Math.sin((p.age + p.rot) * .055) * .006;\n        p.vy -= .004;\n      } else {\n        p.vy += .018;\n      }\n\n      p.rot += p.spin || 0;\n      if (p.age >= p.life) particles.splice(i, 1);\n    }\n\n    for (var w = clearWaves.length - 1; w >= 0; w--) {\n      clearWaves[w].age++;\n      if (clearWaves[w].age >= clearWaves[w].life) clearWaves.splice(w, 1);\n    }\n\n    for (var g = impactGlows.length - 1; g >= 0; g--) {\n      impactGlows[g].age++;\n      if (impactGlows[g].age >= impactGlows[g].life) impactGlows.splice(g, 1);\n    }\n\n    for (var a = 0; a < ambientDust.length; a++) {\n      var dust = ambientDust[a];\n      dust.y += dust.speed;\n      dust.x += Math.sin(Date.now() * .001 + dust.drift) * (dust.type === \"bubble\" ? .10 : .04);\n      dust.rot += dust.spin || 0;\n      dust.twinkle += .018;\n      if (dust.y > H + 12) {\n        dust.y = -14;\n        dust.x = Math.random() * W;\n      }\n    }\n  }\n\n  function unlockAudio() {\n    if (muted) return;\n    if (!audioCtx) {\n      try { audioCtx = new (window.AudioContext || window.webkitAudioContext)(); } catch (e) { audioCtx = null; }\n    }\n    if (((__mmf_bool) => __mmf_bool ? (audioCtx.state === \"suspended\") : __mmf_bool)(audioCtx)) {\n      var resuming = audioCtx.resume();\n      if (((__mmf_bool) => __mmf_bool ? (typeof resuming.catch === \"function\") : __mmf_bool)(resuming)) resuming.catch(function () {});\n    }\n  }\n\n  function tone(freq, dur, type, vol, delay, slide) {\n    if (muted || !audioCtx) return;\n    var now = audioCtx.currentTime + (delay || 0);\n    var osc = audioCtx.createOscillator();\n    var gain = audioCtx.createGain();\n    var finalVol = Math.max(.0002, (vol || .04) * SFX_VOLUME);\n\n    osc.type = type || \"sine\";\n    osc.frequency.setValueAtTime(freq, now);\n    if (slide) osc.frequency.exponentialRampToValueAtTime(Math.max(40, freq * slide), now + dur);\n    gain.gain.setValueAtTime(.0001, now);\n    gain.gain.exponentialRampToValueAtTime(finalVol, now + .012);\n    gain.gain.exponentialRampToValueAtTime(.0001, now + dur);\n    osc.connect(gain);\n    gain.connect(audioCtx.destination);\n    osc.start(now);\n    osc.stop(now + dur + .025);\n  }\n\n  function sound(name) {\n    if (muted) return;\n    unlockAudio();\n    if (!audioCtx) return;\n\n    if (name === \"move\") {\n      tone(480, .026, \"triangle\", .010, 0, 1.03);\n    } else if (name === \"rotate\") {\n      tone(620, .045, \"sine\", .014, 0, 1.12);\n      tone(820, .055, \"triangle\", .012, .032, 1.08);\n    } else if (name === \"soft\") {\n      tone(360, .030, \"triangle\", .010, 0, .96);\n    } else if (name === \"drop\") {\n      tone(190, .070, \"triangle\", .022, 0, .74);\n      tone(120, .105, \"sine\", .014, .035, .86);\n    } else if (name === \"lock\") {\n      tone(420, .040, \"triangle\", .014, 0, .94);\n    } else if (name === \"clear\") {\n      tone(680, .070, \"sine\", .028, 0, 1.12);\n      tone(920, .085, \"triangle\", .022, .050, 1.08);\n      tone(1180, .075, \"sine\", .014, .105, 1.04);\n    } else if (name === \"bigClear\") {\n      tone(560, .080, \"triangle\", .034, 0, 1.14);\n      tone(760, .088, \"sine\", .028, .048, 1.12);\n      tone(1040, .130, \"triangle\", .024, .108, 1.08);\n      tone(1320, .105, \"sine\", .018, .172, 1.04);\n    } else if (name === \"pause\") {\n      tone(360, .080, \"sine\", .016, 0, .84);\n    } else if (name === \"resume\" || name === \"start\") {\n      tone(540, .052, \"triangle\", .018, 0, 1.10);\n      tone(780, .070, \"sine\", .017, .050, 1.08);\n    } else if (name === \"over\") {\n      tone(230, .135, \"triangle\", .022, 0, .76);\n      tone(150, .180, \"sine\", .017, .115, .84);\n    }\n  }\n\n  function draw() {\n    ctx.clearRect(0, 0, W, H);\n    drawBackground();\n    drawPanels();\n    drawBoardFrame();\n    drawBoard();\n    drawGhost();\n    drawPiece();\n    drawImpactGlows();\n    drawClearWaves();\n    drawParticles();\n  }\n\n  function drawBackground() {\n    var bg = ctx.createLinearGradient(0, 0, 0, H);\n    bg.addColorStop(0, \"#17142B\");\n    bg.addColorStop(.36, \"#30224B\");\n    bg.addColorStop(.72, \"#5A426C\");\n    bg.addColorStop(1, \"#FFF1C9\");\n    ctx.fillStyle = bg;\n    ctx.fillRect(0, 0, W, H);\n\n    ctx.save();\n\n    function moon(cx, cy, r) {\n      var glow = ctx.createRadialGradient(cx, cy, 2, cx, cy, r * 3.3);\n      glow.addColorStop(0, \"rgba(255, 241, 201, .92)\");\n      glow.addColorStop(.26, \"rgba(255, 241, 201, .34)\");\n      glow.addColorStop(.70, \"rgba(255, 241, 201, .08)\");\n      glow.addColorStop(1, \"rgba(255, 241, 201, 0)\");\n      ctx.fillStyle = glow;\n      ctx.beginPath();\n      ctx.arc(cx, cy, r * 3.3, 0, Math.PI * 2);\n      ctx.fill();\n\n      var body = ctx.createRadialGradient(cx - r * .26, cy - r * .28, 2, cx, cy, r);\n      body.addColorStop(0, \"#FFF8DF\");\n      body.addColorStop(.62, \"#FFF1C9\");\n      body.addColorStop(1, \"#E8C579\");\n      ctx.fillStyle = body;\n      ctx.beginPath();\n      ctx.arc(cx, cy, r, 0, Math.PI * 2);\n      ctx.fill();\n\n      ctx.globalAlpha = .16;\n      ctx.fillStyle = \"#8B5A3B\";\n      ctx.beginPath(); ctx.arc(cx - r * .22, cy + r * .06, r * .18, 0, Math.PI * 2); ctx.fill();\n      ctx.beginPath(); ctx.arc(cx + r * .20, cy - r * .18, r * .12, 0, Math.PI * 2); ctx.fill();\n      ctx.beginPath(); ctx.arc(cx + r * .30, cy + r * .25, r * .08, 0, Math.PI * 2); ctx.fill();\n      ctx.globalAlpha = 1;\n    }\n\n    function softCloud(x, y, s, a) {\n      ctx.save();\n      ctx.globalAlpha = a == null ? .26 : a;\n      ctx.translate(x, y);\n      ctx.scale(s, s);\n      ctx.fillStyle = \"#DCCFF4\";\n      ctx.beginPath();\n      ctx.arc(-28, 8, 23, 0, Math.PI * 2);\n      ctx.arc(-5, -4, 30, 0, Math.PI * 2);\n      ctx.arc(25, 4, 25, 0, Math.PI * 2);\n      ctx.arc(47, 12, 18, 0, Math.PI * 2);\n      ctx.closePath();\n      ctx.fill();\n      ctx.restore();\n    }\n\n    function woodBeam(x, y, w, h, rot) {\n      ctx.save();\n      ctx.translate(x, y);\n      ctx.rotate(rot || 0);\n      var beam = ctx.createLinearGradient(0, 0, 0, h);\n      beam.addColorStop(0, \"#8B5A3B\");\n      beam.addColorStop(.52, \"#5D3A2B\");\n      beam.addColorStop(1, \"#3D2A22\");\n      roundedRect(0, 0, w, h, 8, beam, \"rgba(255, 241, 201, .14)\", 1);\n      ctx.globalAlpha = .20;\n      ctx.strokeStyle = \"#FFF1C9\";\n      ctx.lineWidth = 1;\n      for (var i = 10; i < w; i += 28) {\n        ctx.beginPath();\n        ctx.moveTo(i, 4);\n        ctx.quadraticCurveTo(i + 12, h * .45, i - 5, h - 5);\n        ctx.stroke();\n      }\n      ctx.restore();\n    }\n\n    moon(286, 62, 42);\n    softCloud(58, 118, .68, .22);\n    softCloud(284, 150, .56, .18);\n    softCloud(75, 528, .72, .24);\n    softCloud(294, 505, .60, .20);\n\n    woodBeam(-26, 18, 158, 20, -.18);\n    woodBeam(236, 20, 150, 20, .18);\n\n    ctx.save();\n    ctx.globalAlpha = .55;\n    ctx.strokeStyle = \"rgba(255, 241, 201, .35)\";\n    ctx.lineWidth = 1;\n    for (var s = 0; s < 38; s++) {\n      var sx = (s * 73) % W;\n      var sy = 18 + ((s * 47) % 235);\n      var tw = .45 + Math.sin(Date.now() * .0015 + s) * .25;\n      ctx.globalAlpha = .17 + tw * .18;\n      drawFoamStar(sx, sy, 1.8 + (s % 3), s % 2 ? \"#FFF1C9\" : \"#B9A6DE\", s);\n    }\n    ctx.restore();\n\n    \/\/ Hanging moon lanterns\n    function lantern(x, y, r) {\n      ctx.save();\n      ctx.strokeStyle = \"rgba(255, 241, 201, .30)\";\n      ctx.lineWidth = 1;\n      ctx.beginPath();\n      ctx.moveTo(x, y - 58);\n      ctx.lineTo(x, y - r);\n      ctx.stroke();\n\n      var lg = ctx.createRadialGradient(x, y, 1, x, y, r * 2.2);\n      lg.addColorStop(0, \"rgba(255, 241, 201, .55)\");\n      lg.addColorStop(.55, \"rgba(232, 197, 121, .16)\");\n      lg.addColorStop(1, \"rgba(255, 241, 201, 0)\");\n      ctx.fillStyle = lg;\n      ctx.beginPath();\n      ctx.arc(x, y, r * 2.2, 0, Math.PI * 2);\n      ctx.fill();\n\n      roundedRect(x - r * .70, y - r, r * 1.4, r * 2, r * .55, \"rgba(255, 241, 201, .52)\", \"rgba(185, 140, 91, .55)\", 1.2);\n      ctx.fillStyle = \"rgba(48, 34, 75, .55)\";\n      ctx.beginPath();\n      ctx.arc(x, y, r * .38, 0, Math.PI * 2);\n      ctx.fill();\n      ctx.fillStyle = \"#FFF1C9\";\n      ctx.beginPath();\n      ctx.arc(x - r * .10, y - r * .05, r * .42, Math.PI * .35, Math.PI * 1.65, true);\n      ctx.arc(x + r * .06, y - r * .05, r * .30, Math.PI * 1.65, Math.PI * .35, false);\n      ctx.fill();\n      ctx.restore();\n    }\n    lantern(34, 88, 12);\n    lantern(326, 118, 11);\n\n    \/\/ Side workshop shelves and tiny wooden tools\n    ctx.save();\n    ctx.globalAlpha = .34;\n    roundedRect(12, 195, 54, 118, 16, \"rgba(61, 42, 34, .54)\", \"rgba(255, 241, 201, .16)\", 1);\n    roundedRect(296, 212, 52, 130, 16, \"rgba(61, 42, 34, .48)\", \"rgba(255, 241, 201, .14)\", 1);\n    ctx.strokeStyle = \"rgba(255, 241, 201, .22)\";\n    ctx.lineWidth = 1.4;\n    for (var row = 0; row < 3; row++) {\n      ctx.beginPath(); ctx.moveTo(19, 225 + row * 31); ctx.lineTo(59, 225 + row * 31); ctx.stroke();\n      ctx.beginPath(); ctx.moveTo(304, 242 + row * 32); ctx.lineTo(340, 242 + row * 32); ctx.stroke();\n    }\n    ctx.fillStyle = \"rgba(232, 197, 121, .30)\";\n    for (var t = 0; t < 5; t++) {\n      roundedRect(24 + (t % 2) * 18, 204 + t * 19, 8, 17, 3, \"rgba(232, 197, 121, .28)\", \"rgba(255, 241, 201, .12)\", .8);\n      roundedRect(309 + (t % 2) * 16, 222 + t * 20, 9, 18, 3, \"rgba(185, 140, 91, .32)\", \"rgba(255, 241, 201, .12)\", .8);\n    }\n    ctx.restore();\n\n    \/\/ Floating ambient dust: cream moon motes and tiny cloud shavings\n    for (var i = 0; i < ambientDust.length; i++) {\n      var d = ambientDust[i];\n      var twinkle = .72 + Math.sin(d.twinkle) * .28;\n      ctx.globalAlpha = d.alpha * twinkle;\n      if (d.type === \"page\") {\n        softCloud(d.x, d.y, d.r * .055, .22);\n      } else if (d.type === \"glint\") {\n        drawFoamStar(d.x, d.y, d.r * 1.5, d.color || \"#FFF1C9\", d.rot);\n      } else {\n        ctx.fillStyle = d.color || \"#FFF1C9\";\n        ctx.beginPath();\n        ctx.arc(d.x, d.y, d.r, 0, Math.PI * 2);\n        ctx.fill();\n      }\n    }\n\n    \/\/ Warm wooden workbench at the bottom\n    var bench = ctx.createLinearGradient(0, 512, 0, H);\n    bench.addColorStop(0, \"rgba(139, 90, 59, .92)\");\n    bench.addColorStop(.50, \"rgba(93, 58, 43, .97)\");\n    bench.addColorStop(1, \"rgba(45, 31, 32, 1)\");\n    ctx.globalAlpha = 1;\n    ctx.fillStyle = bench;\n    ctx.beginPath();\n    ctx.moveTo(0, 548);\n    ctx.bezierCurveTo(44, 528, 94, 550, 136, 534);\n    ctx.bezierCurveTo(188, 514, 232, 550, 292, 526);\n    ctx.bezierCurveTo(326, 512, 344, 528, W, 520);\n    ctx.lineTo(W, H);\n    ctx.lineTo(0, H);\n    ctx.closePath();\n    ctx.fill();\n\n    ctx.globalAlpha = .23;\n    ctx.strokeStyle = \"#FFF1C9\";\n    ctx.lineWidth = 1;\n    for (var k = 0; k < 5; k++) {\n      ctx.beginPath();\n      ctx.moveTo(20, 565 + k * 13);\n      ctx.quadraticCurveTo(166, 552 + k * 7, 340, 565 + k * 13);\n      ctx.stroke();\n    }\n\n    \/\/ Mochi bowls and tiny mallets\n    ctx.globalAlpha = .55;\n    roundedRect(38, 552, 72, 34, 18, \"rgba(255, 241, 201, .20)\", \"rgba(255, 241, 201, .22)\", 1);\n    roundedRect(252, 548, 74, 36, 18, \"rgba(255, 241, 201, .18)\", \"rgba(255, 241, 201, .20)\", 1);\n    ctx.fillStyle = \"rgba(255, 241, 201, .58)\";\n    for (var m = 0; m < 6; m++) {\n      ctx.beginPath();\n      ctx.arc(54 + (m % 3) * 17, 558 + Math.floor(m \/ 3) * 10, 7, 0, Math.PI * 2);\n      ctx.fill();\n    }\n    ctx.fillStyle = \"rgba(232, 197, 121, .48)\";\n    for (var n = 0; n < 5; n++) {\n      ctx.beginPath();\n      ctx.arc(267 + (n % 3) * 16, 557 + Math.floor(n \/ 3) * 11, 7, 0, Math.PI * 2);\n      ctx.fill();\n    }\n    ctx.strokeStyle = \"rgba(255, 241, 201, .36)\";\n    ctx.lineWidth = 4;\n    ctx.beginPath(); ctx.moveTo(115, 560); ctx.lineTo(142, 575); ctx.stroke();\n    ctx.beginPath(); ctx.moveTo(224, 570); ctx.lineTo(249, 552); ctx.stroke();\n\n    ctx.restore();\n  }\n\n\n\n  function drawCloud(x, y, scale, fill) {\n    ctx.save();\n    ctx.translate(x, y);\n    ctx.scale(scale, scale);\n    ctx.fillStyle = fill || \"rgba(255,248,223,.34)\";\n    ctx.beginPath();\n    ctx.arc(-34, 8, 24, 0, Math.PI * 2);\n    ctx.arc(-10, -6, 31, 0, Math.PI * 2);\n    ctx.arc(24, 3, 27, 0, Math.PI * 2);\n    ctx.arc(48, 13, 19, 0, Math.PI * 2);\n    ctx.closePath();\n    ctx.fill();\n    ctx.restore();\n  }\n\n  function drawBubble(x, y, r, color) {\n    ctx.save();\n    ctx.strokeStyle = color || \"#FFF8DF\";\n    ctx.lineWidth = Math.max(.8, r * .16);\n    ctx.globalAlpha *= .86;\n    ctx.beginPath();\n    ctx.arc(x, y, r, 0, Math.PI * 2);\n    ctx.stroke();\n    ctx.globalAlpha *= .70;\n    ctx.fillStyle = \"rgba(255,248,223,.18)\";\n    ctx.beginPath();\n    ctx.arc(x - r * .28, y - r * .30, Math.max(1, r * .20), 0, Math.PI * 2);\n    ctx.fill();\n    ctx.restore();\n  }\n\n  function drawFoamStar(x, y, r, color, rot) {\n    ctx.save();\n    ctx.translate(x, y);\n    ctx.rotate(rot || 0);\n    ctx.strokeStyle = color || \"#FFF8DF\";\n    ctx.lineWidth = 1.05;\n    ctx.lineCap = \"round\";\n    ctx.beginPath();\n    ctx.moveTo(-r, 0);\n    ctx.lineTo(r, 0);\n    ctx.moveTo(0, -r);\n    ctx.lineTo(0, r);\n    ctx.stroke();\n    ctx.restore();\n  }\n\n  \n\n  \n\n  \n  function drawWindowArch(x, y, w, h) {\n    ctx.save();\n    ctx.globalAlpha = .22;\n    ctx.strokeStyle = \"#8B674A\";\n    ctx.fillStyle = \"rgba(255,248,234,.22)\";\n    ctx.lineWidth = 2;\n    roundedRect(x, y + 8, w, h, 22, \"rgba(255,248,234,.14)\", \"rgba(111,65,40,.32)\", 1.4);\n    ctx.beginPath();\n    ctx.moveTo(x + w \/ 2, y + 12);\n    ctx.lineTo(x + w \/ 2, y + h + 4);\n    ctx.moveTo(x + 10, y + 72);\n    ctx.lineTo(x + w - 10, y + 72);\n    ctx.stroke();\n    ctx.restore();\n  }\n\n  \n\n  function drawHangingLamp(x, y) {\n    ctx.save();\n    ctx.globalAlpha = .26;\n    ctx.strokeStyle = \"#8B674A\";\n    ctx.lineWidth = 1;\n    ctx.beginPath();\n    ctx.moveTo(x, y);\n    ctx.lineTo(x, y + 50);\n    ctx.stroke();\n    var lamp = ctx.createRadialGradient(x, y + 60, 2, x, y + 60, 38);\n    lamp.addColorStop(0, \"rgba(255,248,234,.58)\");\n    lamp.addColorStop(1, \"rgba(255,248,234,0)\");\n    ctx.fillStyle = lamp;\n    ctx.beginPath();\n    ctx.arc(x, y + 60, 38, 0, Math.PI * 2);\n    ctx.fill();\n    ctx.fillStyle = \"rgba(232,183,200,.45)\";\n    ctx.beginPath();\n    ctx.ellipse(x, y + 55, 16, 9, 0, 0, Math.PI * 2);\n    ctx.fill();\n    ctx.restore();\n  }\n\n  \n\n  \n\n  function drawPanels() {\n    var labels = [[\"Score\", score], [\"Best\", best], [\"Level\", level], [\"Lines\", lines]];\n    var panelW = 64;\n    var gap = 6;\n    var startX = 8;\n    var y = 44;\n\n    for (var i = 0; i < labels.length; i++) {\n      var x = startX + i * (panelW + gap);\n      drawPanelBox(x, y, panelW, 34);\n      ctx.fillStyle = \"rgba(90,50,31,.72)\";\n      ctx.font = \"800 8px system-ui, -apple-system, Segoe UI, sans-serif\";\n      ctx.textAlign = \"center\";\n      ctx.textBaseline = \"middle\";\n      ctx.fillText(labels[i][0], x + panelW \/ 2, y + 10);\n      ctx.fillStyle = \"#3F2418\";\n      ctx.font = \"950 13px system-ui, -apple-system, Segoe UI, sans-serif\";\n      ctx.fillText(shortNumber(labels[i][1]), x + panelW \/ 2, y + 24);\n    }\n\n    var nextX = startX + 4 * (panelW + gap);\n    drawPanelBox(nextX, y, panelW, 34);\n    ctx.fillStyle = \"rgba(90,50,31,.72)\";\n    ctx.font = \"800 8px system-ui, -apple-system, Segoe UI, sans-serif\";\n    ctx.textAlign = \"center\";\n    ctx.textBaseline = \"middle\";\n    ctx.fillText(\"Next\", nextX + panelW \/ 2, y + 9);\n    drawNextMini(nextX + panelW \/ 2, y + 23, nextPreview);\n  }\n\n  function drawPanelBox(x, y, w, h) {\n    roundedRect(x, y, w, h, 13, \"rgba(255,248,223,.76)\", \"rgba(90,50,31,.26)\", 1);\n    ctx.save();\n    ctx.globalAlpha = .48;\n    roundedRect(x + 2, y + 2, w - 4, 8, 6, \"rgba(255,248,223,.52)\", \"transparent\", 0);\n    ctx.globalAlpha = .16;\n    ctx.strokeStyle = \"#C69032\";\n    ctx.lineWidth = 1;\n    ctx.beginPath();\n    ctx.moveTo(x + 8, y + h - 6);\n    ctx.quadraticCurveTo(x + w \/ 2, y + h - 2, x + w - 8, y + h - 6);\n    ctx.stroke();\n    ctx.restore();\n  }\n\n  function drawNextMini(cx, cy, piece) {\n    if (!piece) return;\n    var matrix = piece.matrix;\n    var mini = 5.4;\n    var mw = matrix[0].length * mini;\n    var mh = matrix.length * mini;\n    var ox = cx - mw \/ 2;\n    var oy = cy - mh \/ 2 + 1;\n\n    ctx.save();\n    ctx.shadowColor = piece.color;\n    ctx.shadowBlur = 4;\n    for (var y = 0; y < matrix.length; y++) {\n      for (var x = 0; x < matrix[y].length; x++) {\n        if (!matrix[y][x]) continue;\n        roundedRect(ox + x * mini, oy + y * mini, mini - .8, mini - .8, 1.8, piece.color, piece.edge, .8);\n      }\n    }\n    ctx.restore();\n  }\n\n  function shortNumber(n) {\n    if (n >= 1000000) return Math.floor(n \/ 100000) \/ 10 + \"m\";\n    if (n >= 10000) return Math.floor(n \/ 1000) + \"k\";\n    return String(n);\n  }\n\n  function drawBoardFrame() {\n    ctx.save();\n\n    var moonGlow = ctx.createRadialGradient(BOARD_X + BOARD_W \/ 2, BOARD_Y + BOARD_H + 10, 8, BOARD_X + BOARD_W \/ 2, BOARD_Y + BOARD_H + 10, 172);\n    moonGlow.addColorStop(0, \"rgba(255, 241, 201, .26)\");\n    moonGlow.addColorStop(.48, \"rgba(185, 166, 222, .14)\");\n    moonGlow.addColorStop(1, \"rgba(255, 241, 201, 0)\");\n    ctx.fillStyle = moonGlow;\n    ctx.beginPath();\n    ctx.ellipse(BOARD_X + BOARD_W \/ 2, BOARD_Y + BOARD_H + 18, BOARD_W * .62, 34, 0, 0, Math.PI * 2);\n    ctx.fill();\n\n    ctx.shadowColor = \"rgba(18, 13, 35, .52)\";\n    ctx.shadowBlur = 18;\n    ctx.shadowOffsetY = 8;\n    roundedRect(BOARD_X - 13, BOARD_Y - 13, BOARD_W + 26, BOARD_H + 26, 24, \"rgba(48, 34, 75, .72)\", \"rgba(255, 241, 201, .44)\", 1.4);\n\n    ctx.shadowBlur = 0;\n    ctx.shadowOffsetY = 0;\n    var inner = ctx.createLinearGradient(BOARD_X, BOARD_Y - 7, BOARD_X, BOARD_Y + BOARD_H + 7);\n    inner.addColorStop(0, \"rgba(255, 241, 201, .24)\");\n    inner.addColorStop(.52, \"rgba(127, 97, 168, .18)\");\n    inner.addColorStop(1, \"rgba(18, 13, 35, .34)\");\n    roundedRect(BOARD_X - 7, BOARD_Y - 7, BOARD_W + 14, BOARD_H + 14, 18, inner, \"rgba(232, 197, 121, .36)\", 1.5);\n\n    ctx.globalAlpha = .42;\n    roundedRect(BOARD_X - 4, BOARD_Y - 4, BOARD_W + 8, 12, 12, \"rgba(255, 241, 201, .20)\", \"transparent\", 0);\n\n    ctx.globalAlpha = .36;\n    ctx.strokeStyle = \"rgba(255, 241, 201, .72)\";\n    ctx.lineWidth = 1;\n    ctx.beginPath();\n    ctx.moveTo(BOARD_X + 12, BOARD_Y - 2);\n    ctx.quadraticCurveTo(BOARD_X + BOARD_W \/ 2, BOARD_Y + 7, BOARD_X + BOARD_W - 12, BOARD_Y - 2);\n    ctx.stroke();\n\n    ctx.restore();\n  }\n\n  function drawBoard() {\n    ctx.save();\n    roundedRect(BOARD_X, BOARD_Y, BOARD_W, BOARD_H, 16, \"rgba(23, 20, 43, .56)\", \"rgba(255, 241, 201, .23)\", 1);\n    ctx.clip();\n\n    var boardGrad = ctx.createLinearGradient(BOARD_X, BOARD_Y, BOARD_X + BOARD_W, BOARD_Y + BOARD_H);\n    boardGrad.addColorStop(0, \"rgba(48, 34, 75, .58)\");\n    boardGrad.addColorStop(.55, \"rgba(37, 27, 60, .50)\");\n    boardGrad.addColorStop(1, \"rgba(255, 241, 201, .09)\");\n    ctx.fillStyle = boardGrad;\n    ctx.fillRect(BOARD_X, BOARD_Y, BOARD_W, BOARD_H);\n\n    for (var y = 0; y < ROWS; y++) {\n      for (var x = 0; x < COLS; x++) {\n        var px = BOARD_X + x * CELL;\n        var py = BOARD_Y + y * CELL;\n        ctx.fillStyle = ((x + y) % 2 === 0) ? \"rgba(255, 241, 201, .060)\" : \"rgba(185, 166, 222, .045)\";\n        ctx.fillRect(px, py, CELL, CELL);\n        ctx.strokeStyle = \"rgba(255, 241, 201, .082)\";\n        ctx.lineWidth = 1;\n        ctx.strokeRect(px + .5, py + .5, CELL - 1, CELL - 1);\n        if (board[y][x]) drawBlock(px + 2, py + 2, CELL - 4, board[y][x], 1);\n      }\n    }\n\n    ctx.restore();\n  }\n\n  function drawGhost() {\n    if (!canPlay()) return;\n    var gy = ghostY();\n    if (gy === current.y) return;\n\n    ctx.save();\n    ctx.globalAlpha = .38;\n    ctx.setLineDash([4, 4]);\n    current.matrix.forEach(function (row, y) {\n      row.forEach(function (value, x) {\n        if (!value) return;\n        var bx = current.x + x;\n        var by = gy + y;\n        if (by < 0) return;\n        var px = BOARD_X + bx * CELL + 4;\n        var py = BOARD_Y + by * CELL + 4;\n        roundedRect(px, py, CELL - 8, CELL - 8, 6, \"rgba(255,248,234,.045)\", \"rgba(255,248,234,.54)\", 1.5);\n      });\n    });\n    ctx.setLineDash([]);\n    ctx.restore();\n  }\n\n  function drawPiece() {\n    if (!current) return;\n    current.matrix.forEach(function (row, y) {\n      row.forEach(function (value, x) {\n        if (!value) return;\n        var bx = current.x + x;\n        var by = current.y + y;\n        if (by < 0) return;\n        drawBlock(BOARD_X + bx * CELL + 2, BOARD_Y + by * CELL + 2, CELL - 4, current, 1);\n      });\n    });\n  }\n\n  function drawBlock(x, y, size, block, alpha) {\n    ctx.save();\n    ctx.globalAlpha = alpha == null ? 1 : alpha;\n    var g = ctx.createLinearGradient(x, y, x, y + size);\n    g.addColorStop(0, lighten(block.color, .34));\n    g.addColorStop(.50, block.color);\n    g.addColorStop(1, darken(block.color, .14));\n    ctx.shadowColor = block.color;\n    ctx.shadowBlur = 6;\n    roundedRect(x, y, size, size, 7, g, block.edge, 1.15);\n\n    ctx.shadowBlur = 0;\n    ctx.save();\n    ctx.globalAlpha = .36;\n    ctx.beginPath();\n    ctx.moveTo(x + size * .16, y + size * .18);\n    ctx.bezierCurveTo(x + size * .34, y + size * .06, x + size * .62, y + size * .08, x + size * .78, y + size * .16);\n    ctx.lineTo(x + size * .56, y + size * .31);\n    ctx.bezierCurveTo(x + size * .42, y + size * .22, x + size * .26, y + size * .25, x + size * .16, y + size * .18);\n    ctx.closePath();\n    ctx.fillStyle = \"#ffffff\";\n    ctx.fill();\n    ctx.restore();\n\n    ctx.save();\n    ctx.globalAlpha = .18;\n    ctx.strokeStyle = \"#ffffff\";\n    ctx.lineWidth = 1;\n    ctx.beginPath();\n    ctx.moveTo(x + size * .18, y + size * .76);\n    ctx.quadraticCurveTo(x + size * .50, y + size * .34, x + size * .82, y + size * .76);\n    ctx.stroke();\n    ctx.restore();\n\n    drawBlockIcon(x + size \/ 2, y + size \/ 2, size, block.icon);\n    ctx.restore();\n  }\n\n  function drawBlockIcon(cx, cy, size, icon) {\n    ctx.save();\n    ctx.strokeStyle = \"rgba(255, 248, 223, .88)\";\n    ctx.fillStyle = \"rgba(255, 248, 223, .66)\";\n    ctx.lineWidth = 1.35;\n    ctx.lineCap = \"round\";\n    ctx.lineJoin = \"round\";\n\n    if (icon === \"rabbit\") {\n      ctx.fillStyle = \"rgba(255, 248, 223, .76)\";\n      ctx.beginPath();\n      ctx.ellipse(cx, cy + size * .04, size * .18, size * .15, 0, 0, Math.PI * 2);\n      ctx.fill();\n      ctx.beginPath();\n      ctx.ellipse(cx - size * .08, cy - size * .16, size * .055, size * .15, -0.14, 0, Math.PI * 2);\n      ctx.ellipse(cx + size * .08, cy - size * .16, size * .055, size * .15, 0.14, 0, Math.PI * 2);\n      ctx.fill();\n      ctx.fillStyle = \"rgba(48, 34, 75, .42)\";\n      ctx.beginPath();\n      ctx.arc(cx - size * .055, cy + size * .025, size * .018, 0, Math.PI * 2);\n      ctx.arc(cx + size * .055, cy + size * .025, size * .018, 0, Math.PI * 2);\n      ctx.fill();\n      ctx.strokeStyle = \"rgba(255, 241, 201, .78)\";\n      ctx.beginPath();\n      ctx.moveTo(cx - size * .04, cy + size * .09);\n      ctx.quadraticCurveTo(cx, cy + size * .13, cx + size * .04, cy + size * .09);\n      ctx.stroke();\n    } else if (icon === \"mochi\") {\n      ctx.fillStyle = \"rgba(255, 248, 223, .78)\";\n      ctx.beginPath();\n      ctx.ellipse(cx, cy + size * .02, size * .20, size * .14, 0, 0, Math.PI * 2);\n      ctx.fill();\n      ctx.stroke();\n      ctx.fillStyle = \"rgba(242, 184, 162, .70)\";\n      ctx.beginPath();\n      ctx.arc(cx, cy - size * .02, size * .055, 0, Math.PI * 2);\n      ctx.fill();\n      ctx.strokeStyle = \"rgba(139, 90, 59, .28)\";\n      ctx.beginPath();\n      ctx.moveTo(cx - size * .17, cy + size * .15);\n      ctx.quadraticCurveTo(cx, cy + size * .22, cx + size * .17, cy + size * .15);\n      ctx.stroke();\n    } else if (icon === \"lantern\") {\n      ctx.fillStyle = \"rgba(255, 241, 201, .60)\";\n      roundedRect(cx - size * .14, cy - size * .19, size * .28, size * .38, 7, \"rgba(255, 241, 201, .62)\", \"rgba(255, 248, 223, .80)\", 1.1);\n      ctx.strokeStyle = \"rgba(255, 248, 223, .72)\";\n      ctx.beginPath();\n      ctx.moveTo(cx - size * .10, cy - size * .08);\n      ctx.lineTo(cx + size * .10, cy - size * .08);\n      ctx.moveTo(cx - size * .10, cy + size * .05);\n      ctx.lineTo(cx + size * .10, cy + size * .05);\n      ctx.stroke();\n      ctx.fillStyle = \"rgba(127, 97, 168, .45)\";\n      ctx.beginPath();\n      ctx.arc(cx, cy, size * .07, 0, Math.PI * 2);\n      ctx.fill();\n    } else if (icon === \"mallet\") {\n      ctx.save();\n      ctx.translate(cx, cy);\n      ctx.rotate(-.58);\n      roundedRect(-size * .16, -size * .19, size * .32, size * .16, 5, \"rgba(255, 241, 201, .52)\", \"rgba(255, 248, 223, .82)\", 1.1);\n      roundedRect(-size * .035, -size * .03, size * .07, size * .30, 3, \"rgba(255, 241, 201, .58)\", \"rgba(255, 248, 223, .80)\", 1);\n      ctx.restore();\n    } else if (icon === \"cloud\") {\n      ctx.fillStyle = \"rgba(255, 248, 223, .58)\";\n      ctx.beginPath();\n      ctx.arc(cx - size * .13, cy + size * .06, size * .12, 0, Math.PI * 2);\n      ctx.arc(cx, cy - size * .02, size * .16, 0, Math.PI * 2);\n      ctx.arc(cx + size * .15, cy + size * .06, size * .11, 0, Math.PI * 2);\n      ctx.closePath();\n      ctx.fill();\n      ctx.stroke();\n      ctx.strokeStyle = \"rgba(255, 241, 201, .68)\";\n      ctx.beginPath();\n      ctx.moveTo(cx - size * .15, cy + size * .16);\n      ctx.quadraticCurveTo(cx, cy + size * .22, cx + size * .17, cy + size * .15);\n      ctx.stroke();\n    } else if (icon === \"toolbox\") {\n      roundedRect(cx - size * .19, cy - size * .10, size * .38, size * .27, 5, \"rgba(255, 241, 201, .48)\", \"rgba(255, 248, 223, .78)\", 1.1);\n      ctx.beginPath();\n      ctx.arc(cx, cy - size * .11, size * .08, Math.PI, 0);\n      ctx.stroke();\n      ctx.strokeStyle = \"rgba(255, 248, 223, .62)\";\n      ctx.beginPath();\n      ctx.moveTo(cx - size * .12, cy + size * .02);\n      ctx.lineTo(cx + size * .12, cy + size * .02);\n      ctx.stroke();\n    } else if (icon === \"mooncharm\") {\n      ctx.fillStyle = \"rgba(255, 241, 201, .74)\";\n      ctx.beginPath();\n      ctx.arc(cx, cy, size * .16, Math.PI * .35, Math.PI * 1.65, true);\n      ctx.arc(cx + size * .06, cy, size * .11, Math.PI * 1.65, Math.PI * .35, false);\n      ctx.closePath();\n      ctx.fill();\n      ctx.stroke();\n      ctx.strokeStyle = \"rgba(255, 248, 223, .72)\";\n      ctx.beginPath();\n      ctx.moveTo(cx, cy - size * .20);\n      ctx.lineTo(cx, cy - size * .26);\n      ctx.moveTo(cx, cy + size * .16);\n      ctx.lineTo(cx, cy + size * .25);\n      ctx.stroke();\n      drawFoamStar(cx + size * .17, cy - size * .12, size * .045, \"rgba(255, 248, 223, .76)\", 0);\n    }\n\n    ctx.restore();\n  }\n\n  function drawImpactGlows() {\n    ctx.save();\n    for (var i = 0; i < impactGlows.length; i++) {\n      var g = impactGlows[i];\n      var t = g.age \/ g.life;\n      var alpha = 1 - t;\n      ctx.globalAlpha = alpha * .42;\n      var grad = ctx.createRadialGradient(g.x, g.y, 2, g.x, g.y, 44 + t * 26);\n      grad.addColorStop(0, \"rgba(255,248,223,.72)\");\n      grad.addColorStop(.38, g.color);\n      grad.addColorStop(1, \"rgba(255,248,223,0)\");\n      ctx.fillStyle = grad;\n      ctx.beginPath();\n      ctx.ellipse(g.x, g.y, 35 + t * 26, 8 + t * 9, 0, 0, Math.PI * 2);\n      ctx.fill();\n    }\n    ctx.restore();\n  }\n\n  function drawClearWaves() {\n    ctx.save();\n    for (var i = 0; i < clearWaves.length; i++) {\n      var w = clearWaves[i];\n      var t = w.age \/ w.life;\n      var alpha = 1 - t;\n      var width = BOARD_W * (.42 + t * .76);\n      var x = BOARD_X + BOARD_W \/ 2 - width \/ 2;\n      var y = w.y;\n      var grad = ctx.createLinearGradient(x, y, x + width, y);\n      grad.addColorStop(0, \"rgba(255,248,223,0)\");\n      grad.addColorStop(.25, \"rgba(244,197,66,\" + (.34 * alpha) + \")\");\n      grad.addColorStop(.50, \"rgba(255,248,223,\" + (.74 * alpha) + \")\");\n      grad.addColorStop(.72, \"rgba(244,197,66,\" + (.34 * alpha) + \")\");\n      grad.addColorStop(1, \"rgba(255,248,223,0)\");\n\n      ctx.globalAlpha = alpha;\n      ctx.strokeStyle = grad;\n      ctx.lineWidth = 4 + t * 4;\n      ctx.lineCap = \"round\";\n      ctx.beginPath();\n      ctx.moveTo(x, y);\n      ctx.quadraticCurveTo(BOARD_X + BOARD_W \/ 2, y - 4 * Math.sin(t * Math.PI), x + width, y);\n      ctx.stroke();\n\n      ctx.globalAlpha = alpha * .78;\n      for (var d = 0; d < 6; d++) {\n        var px = x + width * (d + .5) \/ 6;\n        var pulse = Math.sin((t + d * .08) * Math.PI);\n        var s = 2.4 + pulse * 2.4;\n        drawBubble(px, y + Math.sin(d) * 2, s, d % 2 ? \"#FFF8DF\" : \"#C69032\");\n      }\n    }\n    ctx.restore();\n  }\n\n  function drawParticles() {\n    ctx.save();\n    for (var i = 0; i < particles.length; i++) {\n      var p = particles[i];\n      var t = p.age \/ p.life;\n      var lifeLeft = 1 - t;\n      ctx.globalAlpha = Math.max(0, lifeLeft);\n\n      if (p.type === \"bubble\") {\n        drawBubble(p.x, p.y, p.r * (.75 + t * .42), p.color);\n      } else if (p.type === \"spark\") {\n        var starSize = p.r * (.55 + lifeLeft * .80);\n        ctx.save();\n        ctx.translate(p.x, p.y);\n        ctx.rotate((p.rot || 0) + t * 1.2);\n        ctx.strokeStyle = p.color;\n        ctx.lineWidth = 1.20;\n        ctx.lineCap = \"round\";\n        ctx.beginPath();\n        ctx.moveTo(-starSize, 0);\n        ctx.lineTo(starSize, 0);\n        ctx.moveTo(0, -starSize);\n        ctx.lineTo(0, starSize);\n        ctx.stroke();\n        ctx.restore();\n      } else if (p.type === \"mist\") {\n        var mistSize = p.r * (.65 + t * 1.25);\n        ctx.save();\n        ctx.translate(p.x, p.y);\n        ctx.rotate(Math.sin(t * Math.PI * 2 + p.rot) * .18);\n        ctx.strokeStyle = p.color;\n        ctx.lineWidth = 1.05;\n        ctx.beginPath();\n        ctx.moveTo(0, mistSize * .65);\n        ctx.quadraticCurveTo(-mistSize * .65, 0, 0, -mistSize * .65);\n        ctx.quadraticCurveTo(mistSize * .52, -mistSize, mistSize * .18, -mistSize * 1.28);\n        ctx.stroke();\n        ctx.restore();\n      } else {\n        ctx.fillStyle = p.color;\n        ctx.beginPath();\n        ctx.arc(p.x, p.y, p.r * lifeLeft, 0, Math.PI * 2);\n        ctx.fill();\n      }\n    }\n    ctx.restore();\n  }\n\n  function roundedRect(x, y, w, h, r, fill, stroke, lineWidth) {\n    ctx.beginPath();\n    ctx.moveTo(x + r, y);\n    ctx.lineTo(x + w - r, y);\n    ctx.quadraticCurveTo(x + w, y, x + w, y + r);\n    ctx.lineTo(x + w, y + h - r);\n    ctx.quadraticCurveTo(x + w, y + h, x + w - r, y + h);\n    ctx.lineTo(x + r, y + h);\n    ctx.quadraticCurveTo(x, y + h, x, y + h - r);\n    ctx.lineTo(x, y + r);\n    ctx.quadraticCurveTo(x, y, x + r, y);\n    ctx.closePath();\n    if (((__mmf_bool) => __mmf_bool ? (fill !== \"transparent\") : __mmf_bool)(fill)) { ctx.fillStyle = fill; ctx.fill(); }\n    if (((__mmf_bool) => __mmf_bool ? (lineWidth) : __mmf_bool)(((__mmf_bool) => __mmf_bool ? (stroke !== \"transparent\") : __mmf_bool)(stroke))) {\n      ctx.strokeStyle = stroke;\n      ctx.lineWidth = lineWidth;\n      ctx.stroke();\n    }\n  }\n\n  function hexToRgb(hex) {\n    var clean = hex.replace(\"#\", \"\");\n    if (clean.length === 3) clean = clean.split(\"\").map(function (c) { return c + c; }).join(\"\");\n    var num = parseInt(clean, 16);\n    return { r: ((__mmf_bits) => (__mmf_bits >>> 0) % 256)(num >> 16), g: ((__mmf_bits) => (__mmf_bits >>> 0) % 256)(num >> 8), b: ((__mmf_bits) => (__mmf_bits >>> 0) % 256)(num) };\n  }\n\n  function rgbToHex(r, g, b) {\n    function h(n) { var s = Math.max(0, Math.min(255, Math.round(n))).toString(16); return s.length === 1 ? \"0\" + s : s; }\n    return \"#\" + h(r) + h(g) + h(b);\n  }\n\n  function lighten(hex, amount) {\n    var c = hexToRgb(hex);\n    return rgbToHex(c.r + (255 - c.r) * amount, c.g + (255 - c.g) * amount, c.b + (255 - c.b) * amount);\n  }\n\n  function darken(hex, amount) {\n    var c = hexToRgb(hex);\n    return rgbToHex(c.r * (1 - amount), c.g * (1 - amount), c.b * (1 - amount));\n  }\n\n  function bindTap(btn, fn) {\n    btn.addEventListener(\"pointerdown\", function (e) {\n      e.preventDefault();\n      unlockAudio();\n      fn();\n    }, { passive: false });\n  }\n\n  function bindHold(btn, key, fn, firstDelay, repeatDelay) {\n    function clear() {\n      if (!holds[key]) return;\n      clearTimeout(holds[key].timeout);\n      clearInterval(holds[key].interval);\n      holds[key] = null;\n    }\n\n    btn.addEventListener(\"pointerdown\", function (e) {\n      e.preventDefault();\n      unlockAudio();\n      clear();\n      fn();\n      holds[key] = {\n        timeout: setTimeout(function () {\n          holds[key].interval = setInterval(fn, repeatDelay);\n        }, firstDelay),\n        interval: null\n      };\n      try { btn.setPointerCapture(e.pointerId); } catch (err) {}\n    }, { passive: false });\n\n    btn.addEventListener(\"pointerup\", clear);\n    btn.addEventListener(\"pointerleave\", clear);\n    btn.addEventListener(\"pointercancel\", clear);\n  }\n\n  function loop(time) {\n    var dt = Math.min(42, time - (lastTime || time));\n    lastTime = time;\n\n    if (canPlay()) {\n      dropTimer += dt;\n      if (dropTimer >= fallDelay()) {\n        stepDown();\n        dropTimer = 0;\n      }\n    }\n\n    if (!document.hidden) {\n      updateParticles();\n      draw();\n    }\n    requestAnimationFrame(loop);\n  }\n\n  canvas.addEventListener(\"pointerdown\", function (e) {\n    if (!canPlay()) return;\n    unlockAudio();\n    e.preventDefault();\n    pointerStart = { x: e.clientX, y: e.clientY, id: e.pointerId };\n    try { canvas.setPointerCapture(e.pointerId); } catch (err) {}\n  }, { passive: false });\n\n  canvas.addEventListener(\"pointerup\", function (e) {\n    if (!pointerStart || !canPlay()) return;\n    e.preventDefault();\n    var dx = e.clientX - pointerStart.x;\n    var dy = e.clientY - pointerStart.y;\n    var adx = Math.abs(dx);\n    var ady = Math.abs(dy);\n    pointerStart = null;\n\n    if (((__mmf_bool) => __mmf_bool ? (ady < 12) : __mmf_bool)(adx < 12)) {\n      rotatePiece();\n      return;\n    }\n\n    if (((__mmf_bool) => __mmf_bool ? (adx > 20) : __mmf_bool)(adx > ady)) {\n      var steps = Math.min(4, Math.max(1, Math.round(adx \/ 34)));\n      var dir = dx > 0 ? 1 : -1;\n      for (var i = 0; i < steps; i++) if (!move(dir)) break;\n      return;\n    }\n\n    if (((__mmf_bool) => __mmf_bool ? (ady > 22) : __mmf_bool)(ady > adx)) {\n      if (dy > 0) softDrop(Math.min(6, Math.max(1, Math.round(ady \/ 32))));\n      else hardDrop();\n    }\n  }, { passive: false });\n\n  canvas.addEventListener(\"pointercancel\", function () { pointerStart = null; });\n\n  root.addEventListener(\"pointerdown\", function () { keyboardActive = true; });\n  root.addEventListener(\"focusin\", function () { keyboardActive = true; });\n  document.addEventListener(\"pointerdown\", function (e) {\n    if (!root.contains(e.target)) keyboardActive = false;\n  });\n  document.addEventListener(\"focusin\", function (e) {\n    if (!root.contains(e.target)) keyboardActive = false;\n  });\n\n  document.addEventListener(\"keydown\", function (e) {\n    if (((__mmf_bool) => __mmf_bool ? (root.classList.contains(\"mmf-fallback-fullscreen\")) : __mmf_bool)(e.key === \"Escape\")) {\n      exitFallbackFullscreen();\n      return;\n    }\n    if (!keyboardActive || e.altKey || e.ctrlKey || e.metaKey || document.hidden) return;\n    var target = e.target;\n    var tag = ((__mmf_bool) => __mmf_bool ? (target.tagName) : __mmf_bool)(target) ? target.tagName.toLowerCase() : \"\";\n    if (tag === \"input\" || tag === \"textarea\" || tag === \"select\" || (((__mmf_bool) => __mmf_bool ? (target.isContentEditable) : __mmf_bool)(target))) return;\n\n    if (e.key === \"p\" || e.key === \"P\") {\n      if (started) {\n        e.preventDefault();\n        if (!e.repeat) togglePause();\n      }\n      return;\n    }\n\n    if (!canPlay()) return;\n    if (e.key === \"ArrowLeft\") {\n      e.preventDefault();\n      move(-1);\n    } else if (e.key === \"ArrowRight\") {\n      e.preventDefault();\n      move(1);\n    } else if (e.key === \"ArrowUp\") {\n      e.preventDefault();\n      if (!e.repeat) rotatePiece();\n    } else if (e.key === \"ArrowDown\") {\n      e.preventDefault();\n      softDrop(1);\n    } else if (e.key === \" \" || e.key === \"Spacebar\") {\n      e.preventDefault();\n      if (!e.repeat) hardDrop();\n    }\n  });\n\n  function runMainButtonAction() {\n    unlockAudio();\n    if (overlayMode === \"resume\") {\n      paused = false;\n      pauseBtn.classList.remove(\"is-paused\");\n      hideOverlay();\n      sound(\"resume\");\n    } else {\n      resetGame();\n      sound(\"start\");\n    }\n  }\n\n  function runSubButtonAction() {\n    unlockAudio();\n    resetGame();\n    sound(\"start\");\n  }\n\n  function bindUiButton(btn, fn) {\n    var lastRun = 0;\n    function run(e) {\n      if (((__mmf_bool) => __mmf_bool ? (e.cancelable) : __mmf_bool)(e)) e.preventDefault();\n      var now = Date.now();\n      if (now - lastRun < 160) return;\n      lastRun = now;\n      fn();\n    }\n    btn.addEventListener(\"pointerdown\", run, { passive: false });\n    btn.addEventListener(\"click\", run);\n  }\n\n  bindUiButton(mainBtn, runMainButtonAction);\n  bindUiButton(subBtn, runSubButtonAction);\n\n  bindUiButton(pauseBtn, function () {\n    unlockAudio();\n    togglePause();\n  });\n\n  bindUiButton(soundBtn, function () {\n    muted = !muted;\n    soundBtn.classList.toggle(\"is-muted\", muted);\n    storageSet(STORAGE_MUTE, muted ? \"1\" : \"0\");\n    if (!muted) {\n      unlockAudio();\n      sound(\"resume\");\n    }\n  });\n\n  function enterFallbackFullscreen() {\n    root.classList.add(\"mmf-fallback-fullscreen\");\n    fitStage();\n  }\n\n  function exitFallbackFullscreen() {\n    root.classList.remove(\"mmf-fallback-fullscreen\");\n    fitStage();\n  }\n\n  bindUiButton(fullscreenBtn, function () {\n    unlockAudio();\n    if (root.classList.contains(\"mmf-fallback-fullscreen\")) {\n      exitFallbackFullscreen();\n      return;\n    }\n    var fs = document.fullscreenElement || document.webkitFullscreenElement;\n    if (fs === root) {\n      try {\n        var exit = document.exitFullscreen || document.webkitExitFullscreen;\n        if (exit) {\n          var finished = exit.call(document);\n          if (((__mmf_bool) => __mmf_bool ? (typeof finished.catch === \"function\") : __mmf_bool)(finished)) finished.catch(function () {});\n        }\n      } catch (error) {}\n    } else {\n      try {\n        var request = root.requestFullscreen || root.webkitRequestFullscreen;\n        if (!request) {\n          enterFallbackFullscreen();\n        } else {\n          var requested = request.call(root);\n          if (((__mmf_bool) => __mmf_bool ? (typeof requested.catch === \"function\") : __mmf_bool)(requested)) {\n            requested.catch(function () { enterFallbackFullscreen(); });\n          }\n        }\n      } catch (error) {\n        enterFallbackFullscreen();\n      }\n    }\n    setTimeout(fitStage, 120);\n  });\n\n  bindHold(leftBtn, \"left\", function () { move(-1); }, 180, 95);\n  bindHold(rightBtn, \"right\", function () { move(1); }, 180, 95);\n  bindTap(rotateBtn, function () { if (canPlay()) rotatePiece(); });\n  bindHold(downBtn, \"down\", function () { softDrop(1); }, 160, 65);\n  bindTap(hardBtn, function () { if (canPlay()) hardDrop(); });\n\n  document.addEventListener(\"visibilitychange\", function () {\n    if (document.hidden) {\n      keyboardActive = false;\n      if (((__mmf_bool) => __mmf_bool ? (!gameOver) : __mmf_bool)(((__mmf_bool) => __mmf_bool ? (!paused) : __mmf_bool)(started))) togglePause();\n    } else {\n      lastTime = performance.now();\n      setTimeout(fitStage, 50);\n    }\n  });\n\n  if (typeof IntersectionObserver !== \"undefined\") {\n    var visibilityObserver = new IntersectionObserver(function (entries) {\n      var entry = entries[0];\n      if (((__mmf_bool) => __mmf_bool ? (!gameOver) : __mmf_bool)(((__mmf_bool) => __mmf_bool ? (!paused) : __mmf_bool)(((__mmf_bool) => __mmf_bool ? (started) : __mmf_bool)(((__mmf_bool) => __mmf_bool ? (entry.intersectionRatio < .10) : __mmf_bool)(entry))))) {\n        togglePause();\n      }\n    }, { threshold: [0, .10] });\n    visibilityObserver.observe(root);\n  }\n\n  window.addEventListener(\"resize\", function () { setTimeout(fitStage, 60); });\n  window.addEventListener(\"orientationchange\", function () { setTimeout(fitStage, 160); });\n  document.addEventListener(\"fullscreenchange\", function () { setTimeout(fitStage, 100); });\n  document.addEventListener(\"webkitfullscreenchange\", function () { setTimeout(fitStage, 100); });\n\n  board = createBoard();\n  createAmbientDust();\n  soundBtn.classList.toggle(\"is-muted\", muted);\n  fitStage();\n\n  showOverlay(\n    \"Mochi Moonfall\",\n    \"Stack moonlit workshop pieces, clear glowing rows, and help the rabbits keep their mochi bench shining.\",\n    \"Start Moonfall\",\n    false,\n    \"start\"\n  );\n\n  requestAnimationFrame(loop);\n  }\n\n  if (document.readyState === \"loading\") {\n    document.addEventListener(\"DOMContentLoaded\", initializeMochiMoonfall, { once: true });\n  } else {\n    initializeMochiMoonfall();\n  }\n})();\n<\/script>\n\n\n\n<h2 class=\"wp-block-heading\">Stack Mochi, Clear Rows, and Keep the Workshop Glowing<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The board is filled with handmade pieces from the moon rabbits' little night workshop. Each block carries a small piece of the theme: a warm lantern, a fluffy rabbit symbol, a wooden mallet, a mochi cake, or a drifting cloud from the midnight sky.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Clearing rows creates a satisfying burst of moonlight, making the game feel magical without becoming too loud or overwhelming. It is simple to understand, but easy to keep playing because every falling piece brings one more chance to fix the board, chase a better score, and keep the workshop alive.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Why You Will Love Mochi Moonfall<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Mochi Moonfall<\/strong> is perfect for players who enjoy relaxing puzzle games with cute visuals, smooth controls, and a little bit of strategy. It works well for short breaks, quiet evenings, or anytime you want a simple game that still keeps your mind focused.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">With responsive controls, fullscreen support, sound control, pause mode, best-score saving, and a warm Moon Rabbit Workshop theme, this game turns a familiar falling-block puzzle into a small magical escape above the clouds.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Mochi Moonfall is a cozy falling-block puzzle game set inside a dreamy moon rabbit workshop above the night clouds. Under soft creamy moonlight, tiny workshop pieces begin to fall: mochi cakes, wooden mallets, moon lanterns, soft clouds, little toolboxes, rabbit charms, and glowing lunar treasures. Your goal is to guide each falling piece into the &hellip;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[10],"tags":[],"class_list":["post-110","post","type-post","status-publish","format-standard","hentry","category-casual"],"_links":{"self":[{"href":"https:\/\/play.produsenpagarbrc.com\/index.php?rest_route=\/wp\/v2\/posts\/110","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=110"}],"version-history":[{"count":0,"href":"https:\/\/play.produsenpagarbrc.com\/index.php?rest_route=\/wp\/v2\/posts\/110\/revisions"}],"wp:attachment":[{"href":"https:\/\/play.produsenpagarbrc.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=110"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/play.produsenpagarbrc.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=110"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/play.produsenpagarbrc.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=110"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}