{"id":41,"date":"2026-10-25T23:34:18","date_gmt":"2026-10-25T23:34:18","guid":{"rendered":"https:\/\/apk.produsenpagarbrc.com\/?p=41"},"modified":"2026-10-25T23:34:18","modified_gmt":"2026-10-25T23:34:18","slug":"quillwick-shelfdash","status":"publish","type":"post","link":"https:\/\/play.produsenpagarbrc.com\/?p=41","title":{"rendered":"Quillwick Shelfdash"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\"><strong>Quillwick Shelfdash<\/strong> is a fast, cozy endless runner set inside the vast passageways of the Honey Library. Players guide Pip, a small archive courier bee, across a moving walnut desk while leaping over book crates, catalogue drawers, and rolling library carts. When the upper paths become crowded with hanging quill racks and long bookmark ribbons, Pip must duck low and race beneath them without losing momentum.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The controls are simple, but the journey becomes steadily more demanding. Jump at the right moment, duck beneath suspended obstacles, collect glowing Golden Page Seals, and use rare library tools to survive increasingly fast archive routes. Every run is different, and every few seconds spent safely travelling through the shelves brings Pip closer to restoring another missing section of the library catalogue.<\/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\/Quillwick-Shelfdash-1024x768.png\" alt=\"\" class=\"wp-image-42\"\/><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">The Story of Pip and the Unfinished Catalogue<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">At the heart of the Honey Library is a catalogue known as the Great Golden Index. It contains the location of every book, letter, map, recipe, field journal, and forgotten manuscript stored throughout the building. Without the index, librarians would have no way to find a requested volume among the library\u2019s endless shelves.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The index is maintained using thousands of Golden Page Seals. Each seal contains a tiny fragment of information: a shelf number, a reading-room name, a collection mark, or a route through the archive. Together, these seals form a complete map of the Honey Library.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">One evening, a powerful draft swept through the upper reading halls. The drawers of the catalogue cabinets opened at once, sending Golden Page Seals spinning through the library corridors. Some landed beneath book carts. Others became trapped between heavy crates, dangling ribbons, and maintenance racks. Before the librarians could gather them, the archive\u2019s moving desks began their nightly delivery cycle.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Pip, the smallest courier in the library, volunteered to retrieve the scattered seals. Equipped with a catalogue satchel, a tiny quill pen, and a pair of determined wings, Pip stepped onto the moving walnut route. The journey would not be easy. The archive passages were filled with storage boxes, rolling carts, hanging tools, and long decorative bookmarks that crossed the path at dangerous heights.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">In <strong>Quillwick Shelfdash<\/strong>, every collected seal restores a piece of the Great Golden Index. The score is more than a simple number. It represents the amount of catalogue knowledge Pip has carried safely back through the library.<\/p>\n\n\n\n<div id=\"quillwick-shelfdash-game\" aria-label=\"Quillwick Shelfdash, a Honey Library endless runner game\">\n  <div class=\"qws-script-warning\" role=\"status\">Game belum aktif. Jika pesan ini tetap terlihat setelah halaman dimuat, periksa Console browser untuk mencari error JavaScript WordPress.<\/div>\n<style>\n#quillwick-shelfdash-game,\n#quillwick-shelfdash-game * {\n  box-sizing: border-box;\n  -webkit-tap-highlight-color: transparent;\n}\n\n#quillwick-shelfdash-game {\n  --qws-cocoa: #2e1a12;\n  --qws-walnut: #4d2f20;\n  --qws-amber: #8d5a2b;\n  --qws-olive: #6f7f4f;\n  --qws-parchment: #fff1c9;\n  --qws-cream: #fff1c9;\n  --qws-ivory: #fff8df;\n  --qws-gold: #f4c542;\n  --qws-wood: #6a4027;\n  --qws-copper: #a45f43;\n  --qws-danger: #c45a4e;\n  --qws-ink: #2e1a12;\n  --qws-muted: #7a6655;\n  width: 100%;\n  max-width: 100%;\n  margin: 16px auto;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  isolation: isolate;\n  color-scheme: light;\n  font-family: Arial, Helvetica, sans-serif;\n  user-select: none;\n  -webkit-user-select: none;\n}\n\nhtml.is-dark #quillwick-shelfdash-game,\nbody.is-dark #quillwick-shelfdash-game {\n  --qws-cocoa: #1c100b;\n  --qws-walnut: #2f1c13;\n  --qws-amber: #4b2f1f;\n  --qws-olive: #80905b;\n  --qws-parchment: #fff3cf;\n  --qws-cream: #f3dfae;\n  --qws-ivory: #f6ecd3;\n  --qws-gold: #dcae36;\n  --qws-wood: #765036;\n  --qws-copper: #b36d50;\n  --qws-ink: #f6ecd3;\n  --qws-muted: #d9c8a5;\n  color-scheme: dark;\n}\n\n#quillwick-shelfdash-game button {\n  margin: 0;\n  border: 0;\n  font: inherit;\n}\n\n#quillwick-shelfdash-game .qws-shell {\n  position: relative;\n  width: min(100%, 1120px, calc((100dvh - 20px) * 16 \/ 9));\n  min-width: 0;\n  margin: 0 auto;\n}\n\n#quillwick-shelfdash-game .qws-frame {\n  position: relative;\n  width: 100%;\n  aspect-ratio: 16 \/ 9;\n  overflow: hidden;\n  border: 1px solid rgba(244, 197, 66, .34);\n  border-radius: clamp(18px, 2.2vw, 28px);\n  background:\n    radial-gradient(circle at 78% 10%, rgba(255, 241, 201, .22), transparent 27%),\n    linear-gradient(150deg, #352117, #68401f 58%, #8a5c2c);\n  box-shadow:\n    inset 0 0 0 1px rgba(255, 255, 255, .10),\n    0 26px 62px rgba(28, 15, 39, .30);\n}\n\nhtml.is-dark #quillwick-shelfdash-game .qws-frame,\nbody.is-dark #quillwick-shelfdash-game .qws-frame {\n  border-color: rgba(244, 197, 66, .26);\n  background:\n    radial-gradient(circle at 78% 10%, rgba(244, 197, 66, .11), transparent 28%),\n    linear-gradient(150deg, #1c100b, #2f1c13 58%, #3b2945);\n  box-shadow:\n    inset 0 0 0 1px rgba(255, 255, 255, .05),\n    0 28px 68px rgba(0, 0, 0, .42);\n}\n\n#quillwick-shelfdash-game .qws-frame.qws-modal-active .qws-canvas {\n  filter: blur(1.6px) brightness(.62) saturate(.88);\n  transform: scale(1.008);\n}\n\n#quillwick-shelfdash-game .qws-canvas {\n  position: absolute;\n  inset: 0;\n  width: 100%;\n  height: 100%;\n  display: block;\n  touch-action: none;\n  transition: filter .2s ease, transform .2s ease;\n}\n\n#quillwick-shelfdash-game .qws-ui {\n  position: absolute;\n  inset: 0;\n  z-index: 5;\n  pointer-events: none;\n}\n\n#quillwick-shelfdash-game .qws-topbar {\n  position: absolute;\n  top: clamp(10px, 1.35vw, 16px);\n  left: clamp(12px, 1.7vw, 20px);\n  right: clamp(12px, 1.7vw, 20px);\n  height: clamp(42px, 5.3vw, 58px);\n  display: flex;\n  align-items: center;\n  justify-content: space-between;\n}\n\n#quillwick-shelfdash-game .qws-top-left,\n#quillwick-shelfdash-game .qws-top-right {\n  display: flex;\n  align-items: center;\n  gap: clamp(6px, .8vw, 10px);\n  pointer-events: auto;\n}\n\n#quillwick-shelfdash-game .qws-icon-button,\n#quillwick-shelfdash-game .qws-action-button,\n#quillwick-shelfdash-game .qws-popup-button {\n  appearance: none;\n  -webkit-appearance: none;\n  cursor: pointer;\n  touch-action: manipulation;\n}\n\n#quillwick-shelfdash-game .qws-icon-button {\n  position: relative;\n  width: clamp(40px, 4.6vw, 50px);\n  height: clamp(40px, 4.6vw, 50px);\n  border: 1px solid rgba(244, 197, 66, .62);\n  border-radius: clamp(12px, 1.4vw, 16px);\n  color: var(--qws-cream);\n  background:\n    radial-gradient(circle at 28% 14%, rgba(255,255,255,.16), transparent 31%),\n    linear-gradient(180deg, var(--qws-amber), var(--qws-walnut));\n  box-shadow:\n    inset 0 1px 0 rgba(255,255,255,.16),\n    0 8px 20px rgba(28,15,39,.24);\n  transition: transform .15s ease, border-color .15s ease, filter .15s ease, box-shadow .15s ease;\n  pointer-events: auto;\n}\n\n#quillwick-shelfdash-game .qws-icon-button:hover,\n#quillwick-shelfdash-game .qws-icon-button:focus-visible,\n#quillwick-shelfdash-game .qws-action-button:hover,\n#quillwick-shelfdash-game .qws-action-button:focus-visible,\n#quillwick-shelfdash-game .qws-popup-button:hover,\n#quillwick-shelfdash-game .qws-popup-button:focus-visible {\n  outline: none;\n  transform: translateY(-2px);\n  border-color: var(--qws-cream);\n  filter: brightness(1.07);\n  box-shadow:\n    0 0 0 3px rgba(244,197,66,.18),\n    0 13px 28px rgba(28,15,39,.30);\n}\n\n#quillwick-shelfdash-game .qws-icon-button:active,\n#quillwick-shelfdash-game .qws-action-button:active,\n#quillwick-shelfdash-game .qws-popup-button:active {\n  transform: translateY(1px) scale(.98);\n}\n\n#quillwick-shelfdash-game button:focus-visible {\n  outline: 3px solid rgba(255,241,201,.92);\n  outline-offset: 3px;\n}\n\n#quillwick-shelfdash-game .qws-score-wrap {\n  position: absolute;\n  left: 50%;\n  top: 50%;\n  transform: translate(-50%, -50%);\n  display: flex;\n  gap: clamp(6px, .8vw, 10px);\n  pointer-events: none;\n}\n\n#quillwick-shelfdash-game .qws-score-box {\n  min-width: clamp(76px, 9vw, 110px);\n  padding: clamp(6px, .75vw, 9px) clamp(8px, 1vw, 14px);\n  border: 1px solid rgba(244,197,66,.48);\n  border-radius: clamp(12px, 1.4vw, 16px);\n  text-align: center;\n  background:\n    radial-gradient(circle at 24% 12%, rgba(255,255,255,.16), transparent 34%),\n    linear-gradient(180deg, rgba(57,36,77,.94), rgba(35,20,47,.94));\n  box-shadow:\n    inset 0 1px 0 rgba(255,255,255,.12),\n    0 8px 20px rgba(28,15,39,.24);\n}\n\n#quillwick-shelfdash-game .qws-score-label {\n  color: var(--qws-parchment);\n  font-size: clamp(7px, .8vw, 10px);\n  line-height: 1;\n  font-weight: 900;\n  letter-spacing: .12em;\n  text-transform: uppercase;\n}\n\n#quillwick-shelfdash-game .qws-score-value {\n  margin-top: 5px;\n  color: var(--qws-cream);\n  font-family: Georgia, \"Times New Roman\", serif;\n  font-size: clamp(13px, 1.6vw, 20px);\n  line-height: 1;\n  font-weight: 900;\n  font-variant-numeric: tabular-nums;\n}\n\n#quillwick-shelfdash-game .qws-power-badge {\n  position: absolute;\n  left: 50%;\n  top: clamp(62px, 7.7vw, 84px);\n  transform: translateX(-50%);\n  min-width: clamp(160px, 21vw, 245px);\n  padding: 7px 13px;\n  border: 1px solid rgba(255,241,201,.62);\n  border-radius: 999px;\n  color: var(--qws-cocoa);\n  background: linear-gradient(90deg, var(--qws-cream), var(--qws-parchment), var(--qws-gold));\n  box-shadow: 0 8px 19px rgba(28,15,39,.22);\n  font-size: clamp(8px, 1vw, 11px);\n  font-weight: 900;\n  letter-spacing: .05em;\n  text-align: center;\n  text-transform: uppercase;\n}\n\n#quillwick-shelfdash-game .qws-power-badge[hidden] {\n  display: none;\n}\n\n#quillwick-shelfdash-game .qws-bottom-controls {\n  position: absolute;\n  left: clamp(20px, 3.8vw, 42px);\n  right: clamp(20px, 3.8vw, 42px);\n  bottom: clamp(16px, 3.2vw, 32px);\n  display: flex;\n  align-items: flex-end;\n  justify-content: space-between;\n}\n\n#quillwick-shelfdash-game .qws-action-button {\n  width: clamp(70px, 8.7vw, 94px);\n  height: clamp(70px, 8.7vw, 94px);\n  border: 2px solid rgba(255,241,201,.86);\n  border-radius: 50%;\n  color: var(--qws-cream);\n  font-size: clamp(11px, 1.3vw, 14px);\n  font-weight: 900;\n  letter-spacing: .10em;\n  background:\n    radial-gradient(circle at 30% 20%, rgba(255,255,255,.24), transparent 28%),\n    linear-gradient(180deg, var(--qws-olive), var(--qws-amber));\n  box-shadow:\n    inset 0 2px 0 rgba(255,255,255,.24),\n    inset 0 -12px 18px rgba(24,12,33,.14),\n    0 13px 28px rgba(28,15,39,.28);\n  pointer-events: auto;\n  transition: transform .14s ease, filter .14s ease, box-shadow .14s ease;\n}\n\n#quillwick-shelfdash-game .qws-action-button:last-child {\n  background:\n    radial-gradient(circle at 30% 20%, rgba(255,255,255,.22), transparent 28%),\n    linear-gradient(180deg, var(--qws-wood), #3f2618);\n}\n\n#quillwick-shelfdash-game .qws-note {\n  position: absolute;\n  left: 50%;\n  bottom: clamp(18px, 3.4vw, 34px);\n  transform: translateX(-50%);\n  max-width: min(66%, 640px);\n  padding: 8px 13px;\n  border: 1px solid rgba(244,197,66,.48);\n  border-radius: 999px;\n  color: var(--qws-cocoa);\n  background: rgba(255,241,201,.95);\n  box-shadow: 0 8px 18px rgba(28,15,39,.22);\n  font-size: clamp(9px, 1.1vw, 12px);\n  line-height: 1.25;\n  text-align: center;\n  opacity: 0;\n  transition: opacity .22s ease;\n}\n\n#quillwick-shelfdash-game .qws-note.qws-show {\n  opacity: 1;\n}\n\n#quillwick-shelfdash-game .qws-overlay {\n  position: absolute;\n  inset: 0;\n  z-index: 10;\n  display: none;\n  align-items: center;\n  justify-content: center;\n  padding: clamp(10px, 2vw, 24px);\n  overflow: hidden;\n  border-radius: inherit;\n  background:\n    radial-gradient(circle at 50% 22%, rgba(255,241,201,.14), transparent 36%),\n    rgba(20, 10, 30, .58);\n  backdrop-filter: blur(6px) saturate(.9);\n  -webkit-backdrop-filter: blur(6px) saturate(.9);\n}\n\n#quillwick-shelfdash-game .qws-overlay.qws-show {\n  display: flex;\n}\n\n#quillwick-shelfdash-game .qws-overlay-fullscreen {\n  position: absolute;\n  top: clamp(11px, 1.5vw, 18px);\n  right: clamp(11px, 1.6vw, 20px);\n  z-index: 40;\n}\n\n#quillwick-shelfdash-game .qws-popup {\n  position: relative;\n  width: min(82%, 420px);\n  max-height: min(84%, 430px);\n  overflow: hidden;\n  padding: clamp(20px, 2.6vw, 30px);\n  border: 1px solid rgba(244,197,66,.62);\n  border-radius: clamp(19px, 2.3vw, 27px);\n  color: var(--qws-cocoa);\n  text-align: center;\n  background:\n    radial-gradient(circle at 82% 2%, rgba(139,120,184,.20), transparent 35%),\n    linear-gradient(155deg, var(--qws-ivory), var(--qws-cream));\n  box-shadow:\n    inset 0 0 0 4px rgba(255,255,255,.22),\n    0 27px 66px rgba(20,10,30,.38);\n  animation: qwsPopupIn .22s ease both;\n  pointer-events: auto;\n}\n\n#quillwick-shelfdash-game .qws-popup::before {\n  content: \"\";\n  display: block;\n  width: 64px;\n  height: 64px;\n  margin: 1px auto 12px;\n  border: 3px solid var(--qws-wood);\n  border-radius: 50% 50% 40% 40%;\n  background:\n    radial-gradient(circle at 50% 60%, var(--qws-cream) 0 14px, var(--qws-copper) 15px 27px, var(--qws-olive) 28px 100%);\n  box-shadow:\n    inset 0 0 0 2px rgba(255,255,255,.22),\n    0 0 24px rgba(139,120,184,.28);\n}\n\n#quillwick-shelfdash-game .qws-popup h2 {\n  margin: 0 0 9px;\n  color: var(--qws-cocoa);\n  font-family: Georgia, \"Times New Roman\", serif;\n  font-size: clamp(21px, 3vw, 34px);\n  line-height: 1.05;\n  font-weight: 900;\n}\n\n#quillwick-shelfdash-game .qws-popup p {\n  max-width: 350px;\n  margin: 0 auto 13px;\n  color: rgba(43,27,52,.80);\n  font-size: clamp(10px, 1.2vw, 13px);\n  line-height: 1.5;\n  font-weight: 650;\n}\n\n#quillwick-shelfdash-game .qws-help {\n  max-width: 350px;\n  margin: 0 auto 14px;\n  padding: 8px 10px;\n  border: 1px solid rgba(139,120,184,.48);\n  border-radius: 14px;\n  color: rgba(43,27,52,.76);\n  background: rgba(139,120,184,.14);\n  font-size: clamp(9px, 1.08vw, 11.5px);\n  line-height: 1.4;\n}\n\n#quillwick-shelfdash-game .qws-popup-actions {\n  display: flex;\n  justify-content: center;\n  gap: 9px;\n  flex-wrap: wrap;\n}\n\n#quillwick-shelfdash-game .qws-popup-button {\n  min-width: 128px;\n  min-height: 42px;\n  padding: 0 17px;\n  border: 1px solid rgba(255,241,201,.84);\n  border-radius: 14px;\n  color: var(--qws-cream);\n  background:\n    radial-gradient(circle at 24% 17%, rgba(255,255,255,.18), transparent 25%),\n    linear-gradient(180deg, var(--qws-amber), var(--qws-walnut));\n  box-shadow:\n    inset 0 1px 0 rgba(255,255,255,.20),\n    0 9px 20px rgba(28,15,39,.24);\n  font-size: 12px;\n  font-weight: 900;\n  letter-spacing: .03em;\n  transition: transform .14s ease, border-color .14s ease, box-shadow .14s ease;\n}\n\n#quillwick-shelfdash-game .qws-popup-button.qws-secondary {\n  background: linear-gradient(180deg, var(--qws-wood), #3f2618);\n}\n\n#quillwick-shelfdash-game .qws-hidden {\n  display: none;\n}\n\n#quillwick-shelfdash-game .qws-icon {\n  position: absolute;\n  inset: 0;\n  margin: auto;\n}\n\n#quillwick-shelfdash-game .qws-pause-icon {\n  width: 20px;\n  height: 22px;\n}\n\n#quillwick-shelfdash-game .qws-pause-icon::before,\n#quillwick-shelfdash-game .qws-pause-icon::after {\n  content: \"\";\n  position: absolute;\n  top: 2px;\n  width: 6px;\n  height: 18px;\n  border-radius: 4px;\n  background: currentColor;\n}\n\n#quillwick-shelfdash-game .qws-pause-icon::before { left: 2px; }\n#quillwick-shelfdash-game .qws-pause-icon::after { right: 2px; }\n\n#quillwick-shelfdash-game .qws-play-icon {\n  width: 21px;\n  height: 22px;\n}\n\n#quillwick-shelfdash-game .qws-play-icon::before {\n  content: \"\";\n  position: absolute;\n  left: 4px;\n  top: 2px;\n  border-top: 9px solid transparent;\n  border-bottom: 9px solid transparent;\n  border-left: 15px solid currentColor;\n}\n\n#quillwick-shelfdash-game .qws-sound-icon {\n  width: 28px;\n  height: 26px;\n}\n\n#quillwick-shelfdash-game .qws-speaker-box {\n  position: absolute;\n  left: 0;\n  top: 9px;\n  width: 7px;\n  height: 9px;\n  border-radius: 3px;\n  background: currentColor;\n}\n\n#quillwick-shelfdash-game .qws-speaker-cone {\n  position: absolute;\n  left: 5px;\n  top: 4px;\n  width: 12px;\n  height: 19px;\n  border-radius: 3px;\n  background: currentColor;\n  clip-path: polygon(0 34%, 100% 0, 100% 100%, 0 66%);\n}\n\n#quillwick-shelfdash-game .qws-sound-wave {\n  position: absolute;\n  border-right: 2px solid currentColor;\n  border-top: 2px solid transparent;\n  border-bottom: 2px solid transparent;\n  border-radius: 50%;\n}\n\n#quillwick-shelfdash-game .qws-wave-1 { right: 7px; top: 10px; width: 7px; height: 8px; }\n#quillwick-shelfdash-game .qws-wave-2 { right: 2px; top: 6px; width: 12px; height: 16px; }\n#quillwick-shelfdash-game .qws-wave-3 { right: -3px; top: 1px; width: 18px; height: 25px; opacity: .72; }\n\n#quillwick-shelfdash-game .qws-sound-slash {\n  position: absolute;\n  left: -2px;\n  top: 11px;\n  width: 27px;\n  height: 4px;\n  border-radius: 99px;\n  background: var(--qws-danger);\n  transform: rotate(-39deg);\n  opacity: 0;\n}\n\n#quillwick-shelfdash-game .qws-muted .qws-sound-wave { opacity: .10; }\n#quillwick-shelfdash-game .qws-muted .qws-sound-slash { opacity: 1; }\n\n#quillwick-shelfdash-game .qws-fullscreen-icon {\n  width: 23px;\n  height: 23px;\n  display: block;\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#quillwick-shelfdash-game.qws-fullscreen-active {\n  position: fixed;\n  inset: 0;\n  z-index: 2147483647;\n  width: 100dvw;\n  height: 100dvh;\n  max-width: none;\n  margin: 0;\n  padding: 0;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  overflow: hidden;\n  background:\n    radial-gradient(circle at 78% 12%, rgba(255,241,201,.14), transparent 28%),\n    linear-gradient(145deg, #1c100b, #3b2518 55%, #654124);\n}\n\n#quillwick-shelfdash-game.qws-fullscreen-active .qws-shell {\n  position: absolute;\n  left: var(--qws-fs-center-x, 50vw);\n  top: var(--qws-fs-center-y, 50vh);\n  width: var(--qws-fs-shell-w, min(100vw, 177.777778vh));\n  height: var(--qws-fs-shell-h, min(100vh, 56.25vw));\n  max-width: none;\n  max-height: none;\n  margin: 0;\n  transform: translate(-50%, -50%);\n}\n\n#quillwick-shelfdash-game.qws-fullscreen-active .qws-frame {\n  width: 100%;\n  height: 100%;\n  aspect-ratio: 16 \/ 9;\n  border: 0;\n  border-radius: 0;\n  box-shadow: none;\n}\n\n#quillwick-shelfdash-game:fullscreen::backdrop,\n#quillwick-shelfdash-game:-webkit-full-screen::backdrop {\n  background: linear-gradient(145deg, #1c100b, #3b2518 55%, #654124);\n}\n\n@keyframes qwsPopupIn {\n  from { opacity: 0; transform: translateY(8px) scale(.97); }\n  to { opacity: 1; transform: translateY(0) scale(1); }\n}\n\n@media (max-width: 680px), (max-height: 430px) {\n  #quillwick-shelfdash-game { margin: 10px auto; }\n  #quillwick-shelfdash-game .qws-frame { border-radius: 18px; }\n  #quillwick-shelfdash-game .qws-topbar { top: 8px; left: 9px; right: 9px; }\n  #quillwick-shelfdash-game .qws-icon-button { width: 39px; height: 39px; border-radius: 12px; }\n  #quillwick-shelfdash-game .qws-score-box { min-width: 60px; padding: 5px 6px 6px; }\n  #quillwick-shelfdash-game .qws-action-button { width: 68px; height: 68px; font-size: 10px; }\n  #quillwick-shelfdash-game .qws-bottom-controls { left: 16px; right: 16px; bottom: 13px; }\n  #quillwick-shelfdash-game .qws-note { bottom: 15px; max-width: 58%; font-size: 9px; }\n  #quillwick-shelfdash-game .qws-power-badge { top: 55px; padding: 5px 9px; }\n  #quillwick-shelfdash-game .qws-popup { width: min(88%, 360px); max-height: 92%; padding: 7px 9px; }\n  #quillwick-shelfdash-game .qws-popup::before { width: 28px; height: 28px; margin: 0 auto 3px; border-width: 2px; }\n  #quillwick-shelfdash-game .qws-popup h2 { margin-bottom: 3px; font-size: 14px; line-height: 1; }\n  #quillwick-shelfdash-game .qws-popup p { display: none; }\n  #quillwick-shelfdash-game .qws-help { margin-bottom: 5px; padding: 4px 6px; font-size: 7px; line-height: 1.18; }\n  #quillwick-shelfdash-game .qws-popup-actions { gap: 4px; }\n  #quillwick-shelfdash-game .qws-popup-button { min-width: 108px; min-height: 28px; padding: 0 8px; border-radius: 10px; font-size: 8px; }\n}\n\n@media (prefers-reduced-motion: reduce) {\n  #quillwick-shelfdash-game *,\n  #quillwick-shelfdash-game *::before,\n  #quillwick-shelfdash-game *::after {\n    animation-duration: .01ms !important;\n    animation-iteration-count: 1 !important;\n    transition-duration: .01ms !important;\n  }\n}\n\n\/* WordPress script diagnostic, automatically removed when the game starts. *\/\n#quillwick-shelfdash-game { position: relative; }\n#quillwick-shelfdash-game .qws-script-warning {\n  position: absolute;\n  z-index: 30;\n  top: 50%;\n  left: 50%;\n  width: min(90%, 470px);\n  transform: translate(-50%, -50%);\n  border: 2px solid #b87333;\n  border-radius: 14px;\n  background: #fff8df;\n  color: #3f2418;\n  padding: 14px 18px;\n  box-shadow: 0 12px 26px rgba(40,20,10,.3);\n  text-align: center;\n  font: 700 13px\/1.5 Arial, sans-serif;\n}\n<\/style>\n\n<div class=\"qws-shell\">\n  <div class=\"qws-frame\">\n    <canvas class=\"qws-canvas\" aria-label=\"Quillwick Shelfdash Honey Library endless runner\"><\/canvas>\n\n    <div class=\"qws-ui\">\n      <div class=\"qws-topbar\">\n        <div class=\"qws-top-left\">\n          <button class=\"qws-icon-button\" type=\"button\" data-qws-action=\"pause\" aria-label=\"Pause game\">\n            <span class=\"qws-icon qws-pause-icon\" data-qws-pause-icon><\/span>\n          <\/button>\n\n          <button class=\"qws-icon-button\" type=\"button\" data-qws-action=\"sound\" aria-label=\"Mute sound\">\n            <span class=\"qws-icon qws-sound-icon\">\n              <span class=\"qws-speaker-box\"><\/span>\n              <span class=\"qws-speaker-cone\"><\/span>\n              <span class=\"qws-sound-wave qws-wave-1\"><\/span>\n              <span class=\"qws-sound-wave qws-wave-2\"><\/span>\n              <span class=\"qws-sound-wave qws-wave-3\"><\/span>\n              <span class=\"qws-sound-slash\"><\/span>\n            <\/span>\n          <\/button>\n        <\/div>\n\n        <div class=\"qws-score-wrap\" aria-live=\"polite\">\n          <div class=\"qws-score-box\">\n            <div class=\"qws-score-label\">Route<\/div>\n            <div class=\"qws-score-value\" data-qws-score>0000<\/div>\n          <\/div>\n          <div class=\"qws-score-box\">\n            <div class=\"qws-score-label\">Best<\/div>\n            <div class=\"qws-score-value\" data-qws-best>0000<\/div>\n          <\/div>\n        <\/div>\n\n        <div class=\"qws-top-right\">\n          <button class=\"qws-icon-button\" type=\"button\" data-qws-action=\"fullscreen\" aria-label=\"Toggle fullscreen\">\n            <span class=\"qws-icon qws-fullscreen-icon\"><\/span>\n          <\/button>\n        <\/div>\n      <\/div>\n\n      <div class=\"qws-power-badge\" data-qws-power hidden><\/div>\n\n      <div class=\"qws-note\" data-qws-note>\n        Collect Golden Page Seals. Honeyglass Shield absorbs one collision, while Quillwind grants one extra midair jump.\n      <\/div>\n\n      <div class=\"qws-bottom-controls\">\n        <button class=\"qws-action-button\" type=\"button\" data-qws-action=\"jump\" aria-label=\"Jump\">JUMP<\/button>\n        <button class=\"qws-action-button\" type=\"button\" data-qws-action=\"duck\" aria-label=\"Duck\">DUCK<\/button>\n      <\/div>\n    <\/div>\n\n    <div class=\"qws-overlay qws-show\" data-qws-overlay>\n      <button class=\"qws-icon-button qws-overlay-fullscreen\" type=\"button\" data-qws-action=\"fullscreen\" aria-label=\"Toggle fullscreen\">\n        <span class=\"qws-icon qws-fullscreen-icon\"><\/span>\n      <\/button>\n\n      <div class=\"qws-popup\" role=\"dialog\" aria-modal=\"true\" aria-labelledby=\"qws-popup-title\">\n        <h2 id=\"qws-popup-title\" data-qws-popup-title>The Golden Shelf Route Opens<\/h2>\n        <p data-qws-popup-text>\n          Guide Pip, a tiny archive bee, along the moving catalogue desk. Leap over book crates, catalogue drawers, and rolling book carts, then duck beneath quill racks and hanging bookmark ribbons while collecting Golden Page Seals.\n        <\/p>\n        <div class=\"qws-help\" data-qws-popup-help>\n          Desktop: press Space, W, or Arrow Up to jump. Press S or Arrow Down to duck. Mobile: use the large lower controls.\n        <\/div>\n        <div class=\"qws-popup-actions\">\n          <button class=\"qws-popup-button\" type=\"button\" data-qws-action=\"main\">Begin the Archive Run<\/button>\n          <button class=\"qws-popup-button qws-secondary qws-hidden\" type=\"button\" data-qws-action=\"restart\">Restart the Archive Route<\/button>\n        <\/div>\n      <\/div>\n    <\/div>\n  <\/div>\n<\/div>\n\n<script>\n(function () {\n      \"use strict\";\n\n      function initializeQuillwickShelfdash() {\n      const root = document.getElementById(\"quillwick-shelfdash-game\");\n      if (!root || root.dataset.qwsReady === \"1\") return;\n      root.dataset.qwsReady = \"1\";\n      const diagnosticNotice = root.querySelector(\".qws-script-warning\");\n      if (diagnosticNotice) diagnosticNotice.remove();\n\n      const frame = root.querySelector(\".qws-frame\");\n      const canvas = root.querySelector(\".qws-canvas\");\n      const ctx = canvas.getContext(\"2d\", { alpha: true });\n\n      const soundButton = root.querySelector('[data-qws-action=\"sound\"]');\n      const pauseIcon = root.querySelector(\"[data-qws-pause-icon]\");\n      const scoreEl = root.querySelector(\"[data-qws-score]\");\n      const bestEl = root.querySelector(\"[data-qws-best]\");\n      const powerEl = root.querySelector(\"[data-qws-power]\");\n      const noteEl = root.querySelector(\"[data-qws-note]\");\n      const overlay = root.querySelector(\"[data-qws-overlay]\");\n      const popupTitle = root.querySelector(\"[data-qws-popup-title]\");\n      const popupText = root.querySelector(\"[data-qws-popup-text]\");\n      const popupHelp = root.querySelector(\"[data-qws-popup-help]\");\n      const mainButton = root.querySelector('[data-qws-action=\"main\"]');\n      const restartButton = root.querySelector('[data-qws-action=\"restart\"]');\n\n      const DESIGN_W = 1280;\n      const DESIGN_H = 720;\n      const GROUND_Y = 578;\n      const BEST_KEY = \"flipa_quillwick_shelfdash_best_v1\";\n      const MUTE_KEY = \"flipa_quillwick_shelfdash_muted_v1\";\n\n      const state = {\n        mode: \"start\",\n        muted: safeGet(MUTE_KEY) === \"1\",\n        score: 0,\n        best: parseInt(safeGet(BEST_KEY) || \"0\", 10) || 0,\n        speed: 450,\n        targetSpeed: 450,\n        elapsed: 0,\n        spawnTimer: 1.55,\n        honeyglassShieldTimer: 0,\n        honeyglassShieldHits: 0,\n        quillwindTimer: 0,\n        noteTimer: 0,\n        milestone: 0,\n        lastObstacle: \"\",\n        shake: 0,\n        dpr: 1,\n        lastTime: 0,\n        roadOffset: 0\n      };\n\n      const input = {\n        jumpHeld: false,\n        duckHeld: false\n      };\n\n      const world = {\n        player: null,\n        obstacles: [],\n        pickups: [],\n        particles: []\n      };\n\n      let audio = null;\n      let resizeTimer = 0;\n      let fallbackFullscreen = false;\n\n      function safeGet(key) {\n        try { return localStorage.getItem(key); } catch (error) { return null; }\n      }\n\n      function safeSet(key, value) {\n        try { localStorage.setItem(key, value); } catch (error) {}\n      }\n\n      function clamp(value, min, max) {\n        return Math.max(min, Math.min(max, value));\n      }\n\n      function random(min, max) {\n        return Math.random() * (max - min) + min;\n      }\n\n      function chance(value) {\n        return Math.random() < value;\n      }\n\n      function darkTheme() {\n        return document.documentElement.classList.contains(\"is-dark\") ||\n          document.body.classList.contains(\"is-dark\") ||\n          document.documentElement.getAttribute(\"data-theme\") === \"dark\" ||\n          document.body.getAttribute(\"data-theme\") === \"dark\";\n      }\n\n      function formatScore(value) {\n        const number = Math.floor(value);\n        return number < 10000 ? String(number).padStart(4, \"0\") : String(number);\n      }\n\n      function createPlayer() {\n        return {\n          x: 184,\n          y: GROUND_Y - 128,\n          w: 82,\n          h: 128,\n          baseW: 82,\n          baseH: 128,\n          vy: 0,\n          grounded: true,\n          ducking: false,\n          usedDouble: false,\n          phase: 0,\n          hold: 0\n        };\n      }\n\n      function resetRun() {\n        world.player = createPlayer();\n        world.obstacles = [];\n        world.pickups = [];\n        world.particles = [];\n        state.score = 0;\n        state.speed = 450;\n        state.targetSpeed = 450;\n        state.elapsed = 0;\n        state.spawnTimer = 1.55;\n        state.honeyglassShieldTimer = 0;\n        state.honeyglassShieldHits = 0;\n        state.quillwindTimer = 0;\n        state.noteTimer = 3.2;\n        state.milestone = 0;\n        state.lastObstacle = \"\";\n        state.shake = 0;\n        state.roadOffset = 0;\n        input.jumpHeld = false;\n        input.duckHeld = false;\n        updateHud();\n      }\n\n      function resize() {\n        syncFullscreenMetrics();\n        const rect = frame.getBoundingClientRect();\n        const cssW = Math.max(1, frame.offsetWidth || rect.width || DESIGN_W);\n        const cssH = Math.max(1, frame.offsetHeight || rect.height || DESIGN_H);\n        state.dpr = Math.min(window.devicePixelRatio || 1, 2);\n        canvas.width = Math.max(1, Math.round(cssW * state.dpr));\n        canvas.height = Math.max(1, Math.round(cssH * state.dpr));\n        canvas.style.width = cssW + \"px\";\n        canvas.style.height = cssH + \"px\";\n        ctx.setTransform(canvas.width \/ DESIGN_W, 0, 0, canvas.height \/ DESIGN_H, 0, 0);\n        ctx.imageSmoothingEnabled = true;\n        if (!world.player) world.player = createPlayer();\n      }\n\n      function scheduleResize(delay) {\n        clearTimeout(resizeTimer);\n        resizeTimer = setTimeout(resize, delay || 0);\n      }\n\n      function ensureAudio() {\n        if (state.muted) return null;\n        try {\n          if (!audio) {\n            const AudioClass = window.AudioContext || window.webkitAudioContext;\n            if (!AudioClass) return null;\n            audio = new AudioClass();\n          }\n          if (audio.state === \"suspended\") audio.resume();\n          return audio;\n        } catch (error) {\n          return null;\n        }\n      }\n\n      function tone(frequency, duration, volume, endFrequency, type) {\n        if (state.muted) return;\n        const ac = ensureAudio();\n        if (!ac) return;\n        const now = ac.currentTime;\n        const oscillator = ac.createOscillator();\n        const gain = ac.createGain();\n        oscillator.type = type || \"sine\";\n        oscillator.frequency.setValueAtTime(frequency, now);\n        if (endFrequency) oscillator.frequency.exponentialRampToValueAtTime(Math.max(20, endFrequency), now + duration);\n        gain.gain.setValueAtTime(.0001, now);\n        gain.gain.exponentialRampToValueAtTime(clamp(volume || .016, .0001, .08), now + .012);\n        gain.gain.exponentialRampToValueAtTime(.0001, now + duration);\n        oscillator.connect(gain);\n        gain.connect(ac.destination);\n        oscillator.start(now);\n        oscillator.stop(now + duration + .03);\n      }\n\n      function sound(type) {\n        if (type === \"click\") tone(420, .07, .012, 540, \"triangle\");\n        else if (type === \"jump\") { tone(390, .1, .016, 690, \"triangle\"); setTimeout(function () { tone(850, .07, .009, 1040); }, 38); }\n        else if (type === \"double\") { tone(570, .09, .018, 920, \"triangle\"); setTimeout(function () { tone(1120, .1, .011, 1420); }, 45); }\n        else if (type === \"duck\") tone(220, .075, .014, 155, \"triangle\");\n        else if (type === \"seal\") { tone(810, .065, .014, 1060); setTimeout(function () { tone(1270, .07, .009, 1480, \"triangle\"); }, 34); }\n        else if (type === \"power\") { tone(470, .09, .017, 690, \"triangle\"); setTimeout(function () { tone(760, .1, .013, 1020); }, 55); }\n        else if (type === \"shield\") { tone(280, .09, .018, 190, \"triangle\"); setTimeout(function () { tone(520, .12, .012, 720); }, 58); }\n        else if (type === \"hit\") { tone(165, .19, .024, 92); setTimeout(function () { tone(108, .12, .013, 76, \"triangle\"); }, 52); }\n        else if (type === \"milestone\") { tone(480, .075, .017, 630, \"triangle\"); setTimeout(function () { tone(740, .085, .014, 950); }, 56); setTimeout(function () { tone(1090, .12, .012, 1460); }, 118); }\n      }\n\n      function updateHud() {\n        scoreEl.textContent = formatScore(state.score);\n        bestEl.textContent = formatScore(state.best);\n\n        const parts = [];\n\n        if ((state.mode === \"playing\" || state.mode === \"paused\") ? (state.honeyglassShieldTimer > 0 ? state.honeyglassShieldHits > 0 : false) : false) {\n          parts.push(\"Honeyglass Shield \" + Math.max(1, Math.ceil(state.honeyglassShieldTimer)));\n        }\n\n        if ((state.mode === \"playing\" || state.mode === \"paused\") ? state.quillwindTimer > 0 : false) {\n          parts.push(\"Quillwind \" + Math.max(1, Math.ceil(state.quillwindTimer)));\n        }\n\n        powerEl.textContent = parts.join(\"  \u2022  \");\n        powerEl.hidden = parts.length === 0;\n      }\n\n      function setMode(mode) {\n        state.mode = mode;\n\n        if (mode === \"playing\") {\n          overlay.classList.remove(\"qws-show\");\n          frame.classList.remove(\"qws-modal-active\");\n          pauseIcon.className = \"qws-icon qws-pause-icon\";\n        } else {\n          overlay.classList.add(\"qws-show\");\n          frame.classList.add(\"qws-modal-active\");\n          pauseIcon.className = mode === \"paused\" ? \"qws-icon qws-play-icon\" : \"qws-icon qws-pause-icon\";\n\n          if (mode === \"start\") {\n            popupTitle.textContent = \"The Golden Shelf Route Opens\";\n            popupText.textContent = \"Guide Pip, a tiny archive bee, along the moving catalogue desk. Leap over book crates, catalogue drawers, and rolling book carts, then duck beneath quill racks and hanging bookmark ribbons while collecting Golden Page Seals.\";\n            popupHelp.textContent = \"Desktop: press Space, W, or Arrow Up to jump. Press S or Arrow Down to duck. Mobile: use the large lower controls.\";\n            mainButton.textContent = \"Begin the Archive Run\";\n            restartButton.classList.add(\"qws-hidden\");\n          } else if (mode === \"paused\") {\n            popupTitle.textContent = \"The Reading Bell Is Quiet\";\n            popupText.textContent = \"Pip waits beside the walnut catalogue desk while the Honey Library rests between deliveries.\";\n            popupHelp.textContent = \"Honeyglass Shield protects against one collision. Quillwind grants one additional midair jump.\";\n            mainButton.textContent = \"Return to the Shelves\";\n            restartButton.textContent = \"Restart the Archive Route\";\n            restartButton.classList.remove(\"qws-hidden\");\n          } else {\n            popupTitle.textContent = \"The Archive Route Closed\";\n            popupText.textContent = \"Pip stumbled before the next reading alcove, but the Honey Library is ready for another delivery.\";\n            popupHelp.textContent = \"Your best archive distance is saved automatically in this browser.\";\n            mainButton.textContent = \"Run the Shelves Again\";\n            restartButton.classList.add(\"qws-hidden\");\n          }\n        }\n\n        updateHud();\n      }\n\n      function startGame() {\n        sound(\"click\");\n        resetRun();\n        setMode(\"playing\");\n        state.lastTime = performance.now();\n        noteEl.classList.add(\"qws-show\");\n      }\n\n      function pauseGame() {\n        if (state.mode !== \"playing\") return;\n        sound(\"click\");\n        setMode(\"paused\");\n      }\n\n      function resumeGame() {\n        if (state.mode !== \"paused\") return;\n        sound(\"click\");\n        setMode(\"playing\");\n        state.lastTime = performance.now();\n      }\n\n      function gameOver() {\n        if (state.mode !== \"playing\") return;\n        if (state.score > state.best) {\n          state.best = Math.floor(state.score);\n          safeSet(BEST_KEY, state.best);\n        }\n        state.shake = .22;\n        sound(\"hit\");\n        setMode(\"gameover\");\n      }\n\n      function emit(x, y, count, colors, spread) {\n        const palette = colors || [\"#f4e4bd\", \"#d9b15d\", \"#b87333\"];\n        for (let i = 0; i < count; i++) {\n          const life = random(.34, .78);\n          world.particles.push({\n            x: x,\n            y: y,\n            vx: random(-78, 78) * (spread || 1),\n            vy: random(-135, 12) * (spread || 1),\n            life: life,\n            maxLife: life,\n            size: random(2.5, 7),\n            color: palette[(Math.random() * palette.length) | 0],\n            star: chance(.42)\n          });\n        }\n      }\n\n      function jump() {\n        const player = world.player;\n        if (state.mode === \"start\" || state.mode === \"gameover\") {\n          startGame();\n          return;\n        }\n        if (state.mode !== \"playing\" || !player) return;\n\n        if (player.grounded) {\n          player.grounded = false;\n          player.usedDouble = false;\n          player.vy = -735;\n          player.hold = .12;\n          input.jumpHeld = true;\n          sound(\"jump\");\n          emit(player.x + player.w * .45, GROUND_Y - 4, 7, [\"#a45f43\", \"#f4e4c9\", \"#d9b15d\"], .9);\n        } else if (state.quillwindTimer > 0 ? !player.usedDouble : false) {\n          player.usedDouble = true;\n          player.vy = -640;\n          player.hold = .08;\n          input.jumpHeld = true;\n          sound(\"double\");\n          emit(player.x + player.w * .48, player.y + player.h * .75, 12, [\"#f4e4bd\", \"#d9b15d\", \"#b87333\"], 1.2);\n        }\n      }\n\n      function setDuck(active) {\n        const player = world.player;\n        input.duckHeld = active;\n        if (active ? (player ? (player.grounded ? (!player.ducking ? state.mode === \"playing\" : false) : false) : false) : false) sound(\"duck\");\n      }\n\n      function spawnGoldenSealTrail(startX) {\n        const count = Math.floor(random(3, 7));\n        const baseY = GROUND_Y - random(140, 215);\n        const arc = random(28, 58);\n        const spacing = 48;\n        for (let i = 0; i < count; i++) {\n          const ratio = count > 1 ? i \/ (count - 1) : 0;\n          world.pickups.push({\n            type: \"goldenSeal\",\n            x: startX + i * spacing,\n            y: baseY - Math.sin(ratio * Math.PI) * arc,\n            r: 16,\n            bob: random(0, Math.PI * 2),\n            spin: random(0, Math.PI * 2)\n          });\n        }\n      }\n\n      function spawnPower(startX) {\n        world.pickups.push({\n          type: chance(.5) ? \"honeyglassShield\" : \"quillwind\",\n          x: startX,\n          y: GROUND_Y - random(180, 242),\n          r: 25,\n          bob: random(0, Math.PI * 2),\n          spin: random(0, Math.PI * 2)\n        });\n      }\n\n      function spawnObstacle() {\n        const choices = [\"bookCrates\", \"catalogDrawers\"];\n\n        if (state.score >= 130) choices.push(\"bookCart\");\n        if (state.score >= 230) choices.push(\"quillRack\", \"bookmarkBanners\", \"quillRack\", \"bookmarkBanners\");\n\n        let kind = choices[(Math.random() * choices.length) | 0];\n\n        if (kind === state.lastObstacle ? chance(.7) : false) {\n          kind = choices[(Math.random() * choices.length) | 0];\n        }\n\n        let obstacle;\n\n        if (kind === \"bookCrates\") {\n          const w = random(94, 122);\n          const h = random(90, 122);\n          obstacle = {\n            kind: kind,\n            lane: \"ground\",\n            x: DESIGN_W + w + random(18, 48),\n            y: GROUND_Y - h,\n            w: w,\n            h: h,\n            hit: { x:.09, y:.10, w:.82, h:.87 }\n          };\n        } else if (kind === \"catalogDrawers\") {\n          const w = random(104, 136);\n          const h = random(88, 118);\n          obstacle = {\n            kind: kind,\n            lane: \"ground\",\n            x: DESIGN_W + w + random(18, 48),\n            y: GROUND_Y - h,\n            w: w,\n            h: h,\n            hit: { x:.08, y:.08, w:.84, h:.89 }\n          };\n        } else if (kind === \"bookCart\") {\n          const w = random(142, 172);\n          const h = random(92, 118);\n          obstacle = {\n            kind: kind,\n            lane: \"ground\",\n            x: DESIGN_W + w + random(20, 54),\n            y: GROUND_Y - h,\n            w: w,\n            h: h,\n            hit: { x:.07, y:.12, w:.86, h:.80 }\n          };\n        } else {\n          const w = random(178, 232);\n          const lowerEdge = GROUND_Y - random(104, 120);\n          const x = DESIGN_W + w + random(18, 58);\n          obstacle = {\n            kind: kind,\n            lane: \"duck\",\n            x: x,\n            prevX: x,\n            y: 0,\n            w: w,\n            h: lowerEdge,\n            phase: random(0, Math.PI * 2),\n            hit: { x:.04, y:.82, w:.92, h:.18 }\n          };\n        }\n\n        world.obstacles.push(obstacle);\n        state.lastObstacle = kind;\n\n        const difficulty = clamp(state.score \/ 4500, 0, 1);\n        let gap = random(DESIGN_W * (.64 - difficulty * .04), DESIGN_W * (.99 - difficulty * .07));\n\n        if (obstacle.lane === \"duck\") gap += random(DESIGN_W * .08, DESIGN_W * .17);\n        if (chance(.18)) gap += random(DESIGN_W * .12, DESIGN_W * .24);\n        if (chance(.12)) gap -= DESIGN_W * .05;\n\n        gap = clamp(gap, DESIGN_W * .59, DESIGN_W * 1.15);\n\n        if (chance(.49)) spawnGoldenSealTrail(obstacle.x + obstacle.w + random(200, 390));\n        if (state.score > 360 ? chance(.13) : false) spawnPower(obstacle.x + obstacle.w + random(430, 650));\n\n        state.spawnTimer = gap \/ Math.max(1, state.speed);\n      }\n\n      function getPlayerBox() {\n        const p = world.player;\n        return { x:p.x+p.w*.22, y:p.y+p.h*.1, w:p.w*.56, h:p.h*.82 };\n      }\n\n      function getObstacleBox(o) {\n        return { x:o.x+o.w*o.hit.x, y:o.y+o.h*o.hit.y, w:o.w*o.hit.w, h:o.h*o.hit.h };\n      }\n\n      function overlap(a,b) {\n        return a.x < b.x+b.w ? (a.x+a.w > b.x ? (a.y < b.y+b.h ? a.y+a.h > b.y : false) : false) : false;\n      }\n\n      function circleRect(c,r) {\n        const nx = clamp(c.x, r.x, r.x+r.w);\n        const ny = clamp(c.y, r.y, r.y+r.h);\n        const dx = c.x-nx;\n        const dy = c.y-ny;\n        return dx*dx+dy*dy <= c.r*c.r;\n      }\n\n      function obstacleHits(o, playerBox) {\n        if (o.lane !== \"duck\") return overlap(playerBox, getObstacleBox(o));\n        const p = world.player;\n        const box = getObstacleBox(o);\n        const previousX = Number.isFinite(o.prevX) ? o.prevX : o.x;\n        const padding = Math.max(6, state.speed * .03);\n        const left = Math.min(previousX, o.x) - padding;\n        const right = Math.max(previousX + o.w, o.x + o.w) + padding;\n        const horizontal = playerBox.x < right ? playerBox.x + playerBox.w > left : false;\n        if (!horizontal) return false;\n        if (p.grounded ? p.ducking : false) return false;\n        const highJump = !p.grounded ? (p.usedDouble ? playerBox.y + playerBox.h < box.y + box.h * .5 : false) : false;\n        return !highJump;\n      }\n\n      function collect(pickup) {\n        if (pickup.type === \"goldenSeal\") {\n          state.score += 35;\n          sound(\"seal\");\n          emit(pickup.x, pickup.y, 8, [\"#fff8df\",\"#d99a5f\",\"#d9b15d\",\"#5f7045\"], .85);\n        } else if (pickup.type === \"honeyglassShield\") {\n          state.honeyglassShieldTimer = 10;\n          state.honeyglassShieldHits = 1;\n          state.score += 45;\n          sound(\"power\");\n          emit(pickup.x, pickup.y, 14, [\"#d99a5f\",\"#fff8df\",\"#d99a5f\",\"#d9b15d\"], 1.2);\n        } else {\n          state.quillwindTimer = 10;\n          state.score += 45;\n          sound(\"power\");\n          emit(pickup.x, pickup.y, 14, [\"#fff8df\",\"#d9b15d\",\"#5f7045\",\"#a45f43\"], 1.2);\n        }\n      }\n\n      function updateParticles(dt) {\n        for (let i = world.particles.length - 1; i >= 0; i--) {\n          const p = world.particles[i];\n          p.life -= dt;\n          p.x += p.vx * dt;\n          p.y += p.vy * dt;\n          p.vy += 820 * dt;\n          if (p.life <= 0) world.particles.splice(i,1);\n        }\n      }\n\n      function update(dt) {\n        if (state.noteTimer > 0) {\n          state.noteTimer -= dt;\n          if (state.noteTimer <= 0) noteEl.classList.remove(\"qws-show\");\n        }\n        state.shake = Math.max(0, state.shake - dt);\n        if (state.honeyglassShieldTimer > 0) {\n          state.honeyglassShieldTimer = Math.max(0, state.honeyglassShieldTimer - dt);\n          if (state.honeyglassShieldTimer <= 0) state.honeyglassShieldHits = 0;\n        }\n        if (state.quillwindTimer > 0) state.quillwindTimer = Math.max(0, state.quillwindTimer - dt);\n\n        state.elapsed += dt;\n        state.targetSpeed = Math.min(450 + state.elapsed * 11.5, 800);\n        state.speed += (state.targetSpeed - state.speed) * Math.min(1, dt * 3.5);\n        state.roadOffset += state.speed * dt;\n        state.score += dt * 18 + (state.speed \/ DESIGN_W) * 2.4;\n\n        const milestone = Math.floor(state.score \/ 1000);\n        if (milestone > state.milestone) {\n          state.milestone = milestone;\n          sound(\"milestone\");\n          emit(DESIGN_W * .5, 54, 16, [\"#fff8df\",\"#b87333\",\"#718052\",\"#6f7f4f\"], 1.4);\n        }\n\n        const p = world.player;\n        if (p.grounded) {\n          if (input.duckHeld) {\n            p.ducking = true;\n            p.h = p.baseH * .68;\n            p.w = p.baseW * 1.08;\n          } else {\n            p.ducking = false;\n            p.h = p.baseH;\n            p.w = p.baseW;\n          }\n          p.y = GROUND_Y - p.h;\n          p.usedDouble = false;\n        } else {\n          if (input.jumpHeld ? (p.vy < 0 ? p.hold > 0 : false) : false) {\n            p.vy -= 1370 * dt;\n            p.hold -= dt;\n          }\n          p.vy += 1940 * dt;\n          if (input.duckHeld) p.vy += 3150 * dt;\n          p.y += p.vy * dt;\n          if (p.y + p.h >= GROUND_Y) {\n            p.y = GROUND_Y - p.h;\n            p.vy = 0;\n            p.grounded = true;\n            p.usedDouble = false;\n            emit(p.x+p.w*.44, GROUND_Y-4, 8, [\"#b87333\",\"#f4e4bd\",\"#718052\"], .9);\n          }\n        }\n        p.phase += dt * (state.speed \/ 300);\n\n        state.spawnTimer -= dt;\n        if (state.spawnTimer <= 0) spawnObstacle();\n\n        for (let i = world.obstacles.length - 1; i >= 0; i--) {\n          const o = world.obstacles[i];\n          o.prevX = o.x;\n          o.x -= state.speed * dt;\n          if (o.x + o.w < -80) world.obstacles.splice(i,1);\n        }\n\n        for (let i = world.pickups.length - 1; i >= 0; i--) {\n          const pickup = world.pickups[i];\n          pickup.x -= state.speed * dt;\n          pickup.bob += dt * 4;\n          pickup.spin += dt * 7;\n          if (pickup.x + pickup.r < -50) world.pickups.splice(i,1);\n        }\n\n        const playerBox = getPlayerBox();\n\n        for (let i = world.pickups.length - 1; i >= 0; i--) {\n          const pickup = world.pickups[i];\n          const circle = { x:pickup.x, y:pickup.y+Math.sin(pickup.bob)*4, r:pickup.r };\n          if (circleRect(circle, playerBox)) {\n            collect(pickup);\n            world.pickups.splice(i,1);\n          }\n        }\n\n        for (let i = world.obstacles.length - 1; i >= 0; i--) {\n          const o = world.obstacles[i];\n          if (!obstacleHits(o, playerBox)) continue;\n          const box = getObstacleBox(o);\n          if (state.honeyglassShieldHits > 0) {\n            state.honeyglassShieldHits = 0;\n            state.honeyglassShieldTimer = 0;\n            state.score += 35;\n            sound(\"shield\");\n            emit(box.x+box.w*.5, box.y+box.h*.5, 24, [\"#d9b15d\",\"#fff8df\",\"#718052\",\"#b87333\"], 1.5);\n            world.obstacles.splice(i,1);\n          } else {\n            emit(playerBox.x+playerBox.w*.5, playerBox.y+playerBox.h*.5, 26, [\"#fff8df\",\"#a45f43\",\"#b87333\",\"#718052\"], 1.5);\n            gameOver();\n          }\n          break;\n        }\n\n        updateParticles(dt);\n        updateHud();\n      }\n\n      function roundedRect(x,y,w,h,r,fill,stroke) {\n        const radius = Math.min(r,w*.5,h*.5);\n        ctx.beginPath();\n        ctx.moveTo(x+radius,y);\n        ctx.arcTo(x+w,y,x+w,y+h,radius);\n        ctx.arcTo(x+w,y+h,x,y+h,radius);\n        ctx.arcTo(x,y+h,x,y,radius);\n        ctx.arcTo(x,y,x+w,y,radius);\n        ctx.closePath();\n        if (fill) { ctx.fillStyle=fill; ctx.fill(); }\n        if (stroke) { ctx.strokeStyle=stroke; ctx.stroke(); }\n      }\n\n      function drawStar(x,y,r,color,alpha,rotation) {\n        ctx.save();\n        ctx.globalAlpha = alpha == null ? 1 : alpha;\n        ctx.translate(x,y);\n        ctx.rotate(rotation || 0);\n        ctx.fillStyle = color || \"#fff8df\";\n        ctx.beginPath();\n        for (let i=0;i<8;i++) {\n          const rr = i%2===0 ? r : r*.3;\n          const angle = -Math.PI\/2 + i*Math.PI\/4;\n          const px = Math.cos(angle)*rr;\n          const py = Math.sin(angle)*rr;\n          if (i===0) ctx.moveTo(px,py); else ctx.lineTo(px,py);\n        }\n        ctx.closePath();\n        ctx.fill();\n        ctx.restore();\n      }\n\n\n\n\n\n\n\n\n\n\nfunction drawHoneyLibraryBackground() {\n  const dark = darkTheme();\n  const wall = ctx.createLinearGradient(0, 0, 0, DESIGN_H);\n  wall.addColorStop(0, dark ? \"#21130d\" : \"#3a2418\");\n  wall.addColorStop(.58, dark ? \"#342116\" : \"#5b3820\");\n  wall.addColorStop(1, dark ? \"#4a2d1b\" : \"#7b512b\");\n  ctx.fillStyle = wall;\n  ctx.fillRect(0, 0, DESIGN_W, DESIGN_H);\n\n  const lampGlow = ctx.createRadialGradient(1018, 128, 8, 1018, 128, 210);\n  lampGlow.addColorStop(0, \"rgba(255,248,223,.96)\");\n  lampGlow.addColorStop(.30, \"rgba(244,197,66,.45)\");\n  lampGlow.addColorStop(1, \"rgba(244,197,66,0)\");\n  ctx.fillStyle = lampGlow;\n  ctx.fillRect(790, -88, 456, 430);\n\n  ctx.save();\n  ctx.translate(1018, 134);\n  ctx.fillStyle = \"#fff1c9\";\n  ctx.strokeStyle = \"#6a4027\";\n  ctx.lineWidth = 8;\n  ctx.beginPath();\n  ctx.arc(0, 0, 82, Math.PI, 0);\n  ctx.lineTo(82, 116);\n  ctx.lineTo(-82, 116);\n  ctx.closePath();\n  ctx.fill();\n  ctx.stroke();\n\n  ctx.strokeStyle = \"rgba(184,115,51,.48)\";\n  ctx.lineWidth = 4;\n  ctx.beginPath();\n  ctx.moveTo(0, -82);\n  ctx.lineTo(0, 116);\n  ctx.moveTo(-82, 16);\n  ctx.lineTo(82, 16);\n  ctx.moveTo(-68, -32);\n  ctx.lineTo(68, 68);\n  ctx.moveTo(68, -32);\n  ctx.lineTo(-68, 68);\n  ctx.stroke();\n  ctx.restore();\n\n  drawLibraryShelf(28, 112, 258, 382);\n  drawLibraryShelf(994, 112, 258, 382);\n\n  ctx.fillStyle = dark ? \"#2a1a12\" : \"#4a2d1c\";\n  ctx.fillRect(0, 0, DESIGN_W, 58);\n  ctx.fillStyle = \"#b87333\";\n  ctx.fillRect(0, 54, DESIGN_W, 9);\n\n  for (let i = 0; i < 7; i++) {\n    const x = 74 + i * 190;\n    const y = 102 + (i % 2) * 17;\n    ctx.strokeStyle = \"rgba(255,241,201,.44)\";\n    ctx.lineWidth = 2;\n    ctx.beginPath();\n    ctx.moveTo(x, 58);\n    ctx.lineTo(x, y - 18);\n    ctx.stroke();\n\n    const glow = ctx.createRadialGradient(x, y, 1, x, y, 36);\n    glow.addColorStop(0, \"rgba(255,248,223,.96)\");\n    glow.addColorStop(.38, \"rgba(244,197,66,.50)\");\n    glow.addColorStop(1, \"rgba(244,197,66,0)\");\n    ctx.fillStyle = glow;\n    ctx.beginPath();\n    ctx.arc(x, y, 36, 0, Math.PI * 2);\n    ctx.fill();\n\n    roundedRect(x - 12, y - 15, 24, 31, 7, \"#f4c542\", \"#6a4027\");\n    ctx.fillStyle = \"#fff8df\";\n    ctx.beginPath();\n    ctx.arc(x, y, 6, 0, Math.PI * 2);\n    ctx.fill();\n  }\n\n  for (let i = 0; i < 30; i++) {\n    const x = 38 + ((i * 97) % 1200);\n    const y = 76 + ((i * 61) % 390);\n    const r = 1.5 + (i % 3) * .7;\n    ctx.fillStyle = i % 3 === 0 ? \"rgba(244,197,66,.55)\" : \"rgba(255,241,201,.42)\";\n    ctx.beginPath();\n    ctx.arc(x, y, r, 0, Math.PI * 2);\n    ctx.fill();\n  }\n\n  const readingGlow = ctx.createRadialGradient(640, 350, 80, 640, 350, 560);\n  readingGlow.addColorStop(0, \"rgba(255,241,201,.10)\");\n  readingGlow.addColorStop(1, dark ? \"rgba(16,9,5,.40)\" : \"rgba(31,17,9,.22)\");\n  ctx.fillStyle = readingGlow;\n  ctx.fillRect(0, 0, DESIGN_W, DESIGN_H);\n\n  function drawLibraryShelf(x, y, w, h) {\n    roundedRect(x, y, w, h, 18, dark ? \"#382319\" : \"#59361f\", \"#b87333\");\n    ctx.fillStyle = dark ? \"#22150f\" : \"#3a2418\";\n    ctx.fillRect(x + 13, y + 18, w - 26, h - 36);\n\n    for (let row = 0; row < 4; row++) {\n      const shelfY = y + 26 + row * 86;\n      ctx.fillStyle = \"#6a4027\";\n      ctx.fillRect(x + 10, shelfY + 61, w - 20, 10);\n      for (let col = 0; col < 7; col++) {\n        const bw = 22 + ((row + col) % 3) * 5;\n        const bh = 42 + ((row * 7 + col * 5) % 18);\n        const bx = x + 22 + col * 30;\n        const by = shelfY + 60 - bh;\n        const colors = [\"#6f7f4f\", \"#a45f43\", \"#b87333\", \"#5a3822\", \"#8a6d3b\"];\n        roundedRect(bx, by, bw, bh, 3, colors[(row + col) % colors.length], \"rgba(255,241,201,.20)\");\n        ctx.fillStyle = \"rgba(255,241,201,.68)\";\n        ctx.fillRect(bx + 4, by + 8, bw - 8, 2);\n      }\n    }\n  }\n}\n\n\nfunction drawArchiveDesk() {\n  const dark = darkTheme();\n  const desk = ctx.createLinearGradient(0, GROUND_Y - 66, 0, DESIGN_H);\n  desk.addColorStop(0, dark ? \"#765036\" : \"#9a693e\");\n  desk.addColorStop(.48, dark ? \"#58351f\" : \"#6a4027\");\n  desk.addColorStop(1, dark ? \"#2e1a12\" : \"#3f2618\");\n  ctx.fillStyle = desk;\n  ctx.fillRect(0, GROUND_Y - 66, DESIGN_W, DESIGN_H - GROUND_Y + 66);\n\n  ctx.fillStyle = \"#f4c542\";\n  ctx.fillRect(0, GROUND_Y - 7, DESIGN_W, 8);\n  ctx.fillStyle = \"rgba(255,241,201,.60)\";\n  ctx.fillRect(0, GROUND_Y - 11, DESIGN_W, 2);\n\n  const seamOffset = -((state.roadOffset * .58) % 220);\n  ctx.strokeStyle = dark ? \"rgba(255,241,201,.12)\" : \"rgba(255,241,201,.23)\";\n  ctx.lineWidth = 3;\n  for (let i = -2; i < 10; i++) {\n    const x = seamOffset + i * 220;\n    ctx.beginPath();\n    ctx.moveTo(x, GROUND_Y + 2);\n    ctx.lineTo(x + 42, DESIGN_H);\n    ctx.stroke();\n  }\n\n  const plateOffset = -((state.roadOffset * .76) % 138);\n  for (let i = -2; i < 13; i++) {\n    const x = plateOffset + i * 138 + 46;\n    drawBookplateStamp(x, GROUND_Y + 82, 14);\n  }\n\n  ctx.fillStyle = dark ? \"rgba(16,9,5,.48)\" : \"rgba(34,19,10,.25)\";\n  ctx.fillRect(0, DESIGN_H - 28, DESIGN_W, 28);\n\n  function drawBookplateStamp(x, y, r) {\n    ctx.save();\n    ctx.globalAlpha = .34;\n    ctx.strokeStyle = \"#fff1c9\";\n    ctx.lineWidth = 2;\n    ctx.beginPath();\n    ctx.moveTo(x, y - r);\n    ctx.lineTo(x + r * .72, y - r * .28);\n    ctx.lineTo(x + r * .46, y + r);\n    ctx.lineTo(x, y + r * .62);\n    ctx.lineTo(x - r * .46, y + r);\n    ctx.lineTo(x - r * .72, y - r * .28);\n    ctx.closePath();\n    ctx.stroke();\n    ctx.beginPath();\n    ctx.arc(x, y, r * .24, 0, Math.PI * 2);\n    ctx.fillStyle = \"#fff1c9\";\n    ctx.fill();\n    ctx.restore();\n  }\n}\n\n\nfunction drawPlayer() {\n  const p = world.player;\n  if (!p) return;\n\n  const stride = Math.sin(p.phase * 6.2);\n  const lift = (p.grounded ? !p.ducking : false) ? Math.cos(p.phase * 6.2) * 2.5 : 0;\n  const tilt = p.ducking ? -.16 : !p.grounded ? clamp(-p.vy \/ 860, -1, 1) * .09 : stride * .025;\n\n  ctx.save();\n  ctx.translate(p.x + p.w * .5, p.y + lift);\n  ctx.rotate(tilt);\n  ctx.translate(-p.w * .5, 0);\n\n  if (state.honeyglassShieldTimer > 0 ? state.honeyglassShieldHits > 0 : false) {\n    const halo = ctx.createRadialGradient(p.w * .5, p.h * .48, 5, p.w * .5, p.h * .48, p.w);\n    halo.addColorStop(0, \"rgba(255,248,223,.12)\");\n    halo.addColorStop(.52, \"rgba(244,197,66,.34)\");\n    halo.addColorStop(1, \"rgba(244,197,66,0)\");\n    ctx.fillStyle = halo;\n    ctx.strokeStyle = \"rgba(255,241,201,.95)\";\n    ctx.lineWidth = 3;\n    ctx.beginPath();\n    ctx.ellipse(p.w * .5, p.h * .52, p.w * .88, p.h * .80, 0, 0, Math.PI * 2);\n    ctx.fill();\n    ctx.stroke();\n    drawStar(p.w * .83, p.h * .18, 7, \"#f4c542\", .96, p.phase);\n  }\n\n  if (p.grounded) {\n    ctx.fillStyle = \"rgba(31,17,9,.28)\";\n    ctx.beginPath();\n    ctx.ellipse(p.w * .5, p.h * 1.01, p.w * (p.ducking ? .64 : .50), p.h * .065, 0, 0, Math.PI * 2);\n    ctx.fill();\n  }\n\n  ctx.save();\n  ctx.translate(p.w * .5, p.h * .43);\n\n  ctx.fillStyle = \"rgba(255,248,223,.76)\";\n  ctx.strokeStyle = \"rgba(184,115,51,.65)\";\n  ctx.lineWidth = 3;\n  ctx.beginPath();\n  ctx.ellipse(-p.w * .23, -p.h * .02, p.w * .25, p.h * .20, -.55 + stride * .08, 0, Math.PI * 2);\n  ctx.ellipse(p.w * .23, -p.h * .02, p.w * .25, p.h * .20, .55 - stride * .08, 0, Math.PI * 2);\n  ctx.fill();\n  ctx.stroke();\n\n  const bodyGrad = ctx.createLinearGradient(0, -p.h * .22, 0, p.h * .30);\n  bodyGrad.addColorStop(0, \"#f4c542\");\n  bodyGrad.addColorStop(.45, \"#d69b2f\");\n  bodyGrad.addColorStop(1, \"#b87333\");\n  ctx.fillStyle = bodyGrad;\n  ctx.strokeStyle = \"#4d2f20\";\n  ctx.lineWidth = 4;\n  ctx.beginPath();\n  ctx.ellipse(0, p.h * .06, p.w * .31, p.h * .31, 0, 0, Math.PI * 2);\n  ctx.fill();\n  ctx.stroke();\n\n  ctx.strokeStyle = \"#4d2f20\";\n  ctx.lineWidth = p.h * .055;\n  ctx.beginPath();\n  ctx.moveTo(-p.w * .27, -p.h * .02);\n  ctx.quadraticCurveTo(0, p.h * .03, p.w * .27, -p.h * .02);\n  ctx.moveTo(-p.w * .26, p.h * .12);\n  ctx.quadraticCurveTo(0, p.h * .18, p.w * .26, p.h * .12);\n  ctx.stroke();\n\n  ctx.fillStyle = \"#fff8df\";\n  ctx.strokeStyle = \"#4d2f20\";\n  ctx.lineWidth = 3;\n  ctx.beginPath();\n  ctx.arc(0, -p.h * .20, p.w * .24, 0, Math.PI * 2);\n  ctx.fill();\n  ctx.stroke();\n\n  ctx.fillStyle = \"#2e1a12\";\n  ctx.beginPath();\n  ctx.arc(-p.w * .075, -p.h * .22, 4, 0, Math.PI * 2);\n  ctx.arc(p.w * .075, -p.h * .22, 4, 0, Math.PI * 2);\n  ctx.fill();\n\n  ctx.fillStyle = \"#a45f43\";\n  ctx.beginPath();\n  ctx.arc(0, -p.h * .155, 3.5, 0, Math.PI * 2);\n  ctx.fill();\n\n  ctx.strokeStyle = \"#4d2f20\";\n  ctx.lineWidth = 2;\n  ctx.beginPath();\n  ctx.moveTo(-p.w * .08, -p.h * .38);\n  ctx.quadraticCurveTo(-p.w * .18, -p.h * .51, -p.w * .24, -p.h * .46);\n  ctx.moveTo(p.w * .08, -p.h * .38);\n  ctx.quadraticCurveTo(p.w * .18, -p.h * .51, p.w * .24, -p.h * .46);\n  ctx.stroke();\n\n  ctx.fillStyle = \"#f4c542\";\n  ctx.beginPath();\n  ctx.arc(-p.w * .24, -p.h * .46, 3.5, 0, Math.PI * 2);\n  ctx.arc(p.w * .24, -p.h * .46, 3.5, 0, Math.PI * 2);\n  ctx.fill();\n\n  roundedRect(p.w * .13, -p.h * .02, p.w * .30, p.h * .27, 8, \"#6a4027\", \"#f4c542\");\n  ctx.fillStyle = \"#fff1c9\";\n  ctx.fillRect(p.w * .20, p.h * .04, p.w * .16, 3);\n\n  ctx.strokeStyle = \"#fff1c9\";\n  ctx.lineWidth = 4;\n  ctx.lineCap = \"round\";\n  ctx.beginPath();\n  ctx.moveTo(-p.w * .24, p.h * .02);\n  ctx.lineTo(-p.w * (.43 + stride * .03), p.h * .16);\n  ctx.moveTo(p.w * .24, p.h * .02);\n  ctx.lineTo(p.w * (.43 - stride * .03), p.h * .13);\n  ctx.stroke();\n\n  ctx.strokeStyle = \"#4d2f20\";\n  ctx.lineWidth = 5;\n  ctx.beginPath();\n  if (p.ducking) {\n    ctx.moveTo(-p.w * .13, p.h * .29);\n    ctx.lineTo(-p.w * .31, p.h * .36);\n    ctx.moveTo(p.w * .13, p.h * .29);\n    ctx.lineTo(p.w * .31, p.h * .36);\n  } else {\n    ctx.moveTo(-p.w * .13, p.h * .29);\n    ctx.lineTo(-p.w * (.23 + stride * .08), p.h * .45);\n    ctx.moveTo(p.w * .13, p.h * .29);\n    ctx.lineTo(p.w * (.23 - stride * .08), p.h * .45);\n  }\n  ctx.stroke();\n\n  ctx.restore();\n\n  ctx.save();\n  ctx.translate(p.w * .73, p.h * .47);\n  ctx.rotate(-.38);\n  ctx.strokeStyle = \"#f4c542\";\n  ctx.lineWidth = 4;\n  ctx.beginPath();\n  ctx.moveTo(0, 0);\n  ctx.lineTo(25, -12);\n  ctx.stroke();\n  ctx.fillStyle = \"#fff1c9\";\n  ctx.beginPath();\n  ctx.moveTo(19, -10);\n  ctx.quadraticCurveTo(35, -28, 38, -5);\n  ctx.quadraticCurveTo(31, 7, 22, -1);\n  ctx.closePath();\n  ctx.fill();\n  ctx.restore();\n\n  if (state.quillwindTimer > 0) {\n    ctx.strokeStyle = \"rgba(255,241,201,.92)\";\n    ctx.lineWidth = 3;\n    for (let i = 0; i < 3; i++) {\n      ctx.beginPath();\n      ctx.moveTo(p.w * (.18 + i * .20), p.h * .91);\n      ctx.bezierCurveTo(p.w * (.06 + i * .20), p.h * .76, p.w * (.27 + i * .20), p.h * .60, p.w * (.12 + i * .20), p.h * .44);\n      ctx.stroke();\n    }\n  }\n\n  ctx.restore();\n}\n\n\nfunction drawBookCrates(o) {\n  ctx.save();\n  ctx.translate(o.x, o.y);\n  ctx.fillStyle = \"rgba(31,17,9,.25)\";\n  ctx.beginPath();\n  ctx.ellipse(o.w * .5, o.h * 1.03, o.w * .48, o.h * .09, 0, 0, Math.PI * 2);\n  ctx.fill();\n\n  roundedRect(o.w * .03, o.h * .38, o.w * .94, o.h * .54, 12, \"#6a4027\", \"#3f2618\");\n  ctx.strokeStyle = \"rgba(255,241,201,.34)\";\n  ctx.lineWidth = 3;\n  ctx.beginPath();\n  ctx.moveTo(o.w * .12, o.h * .44);\n  ctx.lineTo(o.w * .88, o.h * .84);\n  ctx.moveTo(o.w * .88, o.h * .44);\n  ctx.lineTo(o.w * .12, o.h * .84);\n  ctx.stroke();\n\n  const books = [\n    {x:.09,y:.16,w:.24,h:.34,c:\"#6f7f4f\"},\n    {x:.34,y:.07,w:.22,h:.43,c:\"#a45f43\"},\n    {x:.57,y:.13,w:.28,h:.37,c:\"#b87333\"}\n  ];\n  books.forEach(function (b) {\n    roundedRect(o.w * b.x, o.h * b.y, o.w * b.w, o.h * b.h, 5, b.c, \"#4d2f20\");\n    ctx.fillStyle = \"rgba(255,241,201,.72)\";\n    ctx.fillRect(o.w * (b.x + .04), o.h * (b.y + .08), o.w * (b.w - .08), 3);\n  });\n  ctx.restore();\n}\n\n\nfunction drawCatalogDrawers(o) {\n  ctx.save();\n  ctx.translate(o.x, o.y);\n  ctx.fillStyle = \"rgba(31,17,9,.25)\";\n  ctx.beginPath();\n  ctx.ellipse(o.w * .5, o.h * 1.03, o.w * .49, o.h * .09, 0, 0, Math.PI * 2);\n  ctx.fill();\n\n  roundedRect(o.w * .05, o.h * .12, o.w * .90, o.h * .80, 13, \"#7b4b29\", \"#3f2618\");\n  for (let row = 0; row < 2; row++) {\n    for (let col = 0; col < 3; col++) {\n      const x = o.w * (.11 + col * .28);\n      const y = o.h * (.22 + row * .34);\n      roundedRect(x, y, o.w * .22, o.h * .25, 6, \"#8f5b31\", \"#4d2f20\");\n      roundedRect(x + o.w * .055, y + o.h * .055, o.w * .11, o.h * .07, 3, \"#f4c542\", \"#6a4027\");\n      ctx.fillStyle = \"#4d2f20\";\n      ctx.beginPath();\n      ctx.arc(x + o.w * .11, y + o.h * .18, 3, 0, Math.PI * 2);\n      ctx.fill();\n    }\n  }\n  ctx.restore();\n}\n\n\nfunction drawBookCart(o) {\n  ctx.save();\n  ctx.translate(o.x, o.y);\n  ctx.fillStyle = \"rgba(31,17,9,.25)\";\n  ctx.beginPath();\n  ctx.ellipse(o.w * .5, o.h * 1.04, o.w * .49, o.h * .09, 0, 0, Math.PI * 2);\n  ctx.fill();\n\n  roundedRect(o.w * .05, o.h * .46, o.w * .90, o.h * .30, 10, \"#6a4027\", \"#3f2618\");\n  ctx.fillStyle = \"#f4c542\";\n  ctx.fillRect(o.w * .08, o.h * .42, o.w * .84, o.h * .08);\n\n  const colors = [\"#6f7f4f\", \"#a45f43\", \"#b87333\", \"#5a3822\"];\n  for (let i = 0; i < 5; i++) {\n    const x = o.w * (.10 + i * .16);\n    const h = o.h * (.23 + (i % 3) * .05);\n    roundedRect(x, o.h * .42 - h, o.w * .13, h, 4, colors[i % colors.length], \"#4d2f20\");\n    ctx.fillStyle = \"rgba(255,241,201,.70)\";\n    ctx.fillRect(x + 4, o.h * .42 - h + 7, o.w * .13 - 8, 2);\n  }\n\n  ctx.fillStyle = \"#2e1a12\";\n  ctx.beginPath();\n  ctx.arc(o.w * .24, o.h * .84, o.h * .12, 0, Math.PI * 2);\n  ctx.arc(o.w * .76, o.h * .84, o.h * .12, 0, Math.PI * 2);\n  ctx.fill();\n  ctx.strokeStyle = \"#f4c542\";\n  ctx.lineWidth = 4;\n  ctx.stroke();\n  ctx.restore();\n}\n\n\nfunction drawQuillRack(o) {\n  ctx.save();\n  ctx.translate(o.x, o.y);\n  const lower = o.h;\n  const sway = Math.sin(performance.now() * .0016 + o.phase) * 2.5;\n\n  ctx.fillStyle = \"#3f2618\";\n  ctx.fillRect(0, 0, o.w, 20);\n  ctx.fillStyle = \"#6a4027\";\n  ctx.fillRect(o.w * .06, 0, 12, lower - 84);\n  ctx.fillRect(o.w * .88, 0, 12, lower - 84);\n  roundedRect(o.w * .03, lower - 106, o.w * .94, 54, 11, \"#6a4027\", \"#f4c542\");\n\n  for (let i = 0; i < 5; i++) {\n    const x = o.w * (.12 + i * .19);\n    ctx.strokeStyle = \"rgba(255,241,201,.62)\";\n    ctx.lineWidth = 2;\n    ctx.beginPath();\n    ctx.moveTo(x, lower - 52);\n    ctx.lineTo(x + sway * (i % 2 ? 1 : -1), lower - 10);\n    ctx.stroke();\n\n    ctx.save();\n    ctx.translate(x + sway, lower - 28);\n    ctx.rotate((i - 2) * .08);\n    ctx.fillStyle = i % 2 ? \"#fff1c9\" : \"#f4c542\";\n    ctx.beginPath();\n    ctx.moveTo(0, 20);\n    ctx.quadraticCurveTo(-13, 0, 1, -20);\n    ctx.quadraticCurveTo(17, 1, 0, 20);\n    ctx.fill();\n    ctx.strokeStyle = \"#6a4027\";\n    ctx.lineWidth = 2;\n    ctx.beginPath();\n    ctx.moveTo(0, 20);\n    ctx.lineTo(1, -18);\n    ctx.stroke();\n    ctx.restore();\n  }\n  ctx.restore();\n}\n\nfunction drawBookmarkBanners(o) {\n  ctx.save();\n  ctx.translate(o.x, o.y);\n  const lower = o.h;\n  const sway = Math.sin(performance.now() * .0014 + o.phase) * 2.5;\n\n  ctx.fillStyle = \"#3f2618\";\n  ctx.fillRect(0, 0, o.w, 20);\n  ctx.fillStyle = \"#6a4027\";\n  ctx.fillRect(o.w * .06, 0, 11, lower - 94);\n  ctx.fillRect(o.w * .89, 0, 11, lower - 94);\n\n  ctx.strokeStyle = \"#f4c542\";\n  ctx.lineWidth = 7;\n  ctx.beginPath();\n  ctx.moveTo(o.w * .05, lower - 101);\n  ctx.quadraticCurveTo(o.w * .50, lower - 116, o.w * .95, lower - 101);\n  ctx.stroke();\n\n  const ribbons = [\n    {x:.08,w:.24,c:\"#fff1c9\"},\n    {x:.38,w:.24,c:\"#6f7f4f\"},\n    {x:.68,w:.23,c:\"#a45f43\"}\n  ];\n\n  ribbons.forEach(function (ribbon, index) {\n    const x = o.w * ribbon.x + sway * (index - 1);\n    const y = lower - 98;\n    const w = o.w * ribbon.w;\n    const h = 86 + (index % 2) * 12;\n    ctx.fillStyle = ribbon.c;\n    ctx.strokeStyle = \"rgba(77,47,32,.72)\";\n    ctx.lineWidth = 3;\n    ctx.beginPath();\n    ctx.moveTo(x, y);\n    ctx.lineTo(x + w, y);\n    ctx.lineTo(x + w, y + h);\n    ctx.lineTo(x + w * .5, y + h - 14);\n    ctx.lineTo(x, y + h);\n    ctx.closePath();\n    ctx.fill();\n    ctx.stroke();\n\n    ctx.fillStyle = \"#f4c542\";\n    ctx.beginPath();\n    ctx.arc(x + w * .50, y + h * .42, w * .14, 0, Math.PI * 2);\n    ctx.fill();\n    ctx.fillStyle = \"#4d2f20\";\n    ctx.beginPath();\n    ctx.arc(x + w * .45, y + h * .39, 2.5, 0, Math.PI * 2);\n    ctx.arc(x + w * .55, y + h * .39, 2.5, 0, Math.PI * 2);\n    ctx.fill();\n  });\n  ctx.restore();\n}\n\n\nfunction drawObstacle(o) {\n  if (o.kind === \"bookCrates\") drawBookCrates(o);\n  else if (o.kind === \"catalogDrawers\") drawCatalogDrawers(o);\n  else if (o.kind === \"bookCart\") drawBookCart(o);\n  else if (o.kind === \"quillRack\") drawQuillRack(o);\n  else drawBookmarkBanners(o);\n}\n\n\nfunction drawPickup(pickup) {\n  const y = pickup.y + Math.sin(pickup.bob) * 4;\n\n  if (pickup.type === \"goldenSeal\") {\n    ctx.save();\n    const halo = ctx.createRadialGradient(pickup.x, y, 0, pickup.x, y, pickup.r * 2.8);\n    halo.addColorStop(0, \"rgba(255,248,223,.96)\");\n    halo.addColorStop(.42, \"rgba(244,197,66,.48)\");\n    halo.addColorStop(1, \"rgba(244,197,66,0)\");\n    ctx.fillStyle = halo;\n    ctx.beginPath();\n    ctx.arc(pickup.x, y, pickup.r * 2.8, 0, Math.PI * 2);\n    ctx.fill();\n\n    ctx.translate(pickup.x, y);\n    ctx.rotate(Math.sin(pickup.spin * .12) * .12);\n    ctx.fillStyle = \"#f4c542\";\n    ctx.strokeStyle = \"#6a4027\";\n    ctx.lineWidth = 3;\n    ctx.beginPath();\n    for (let i = 0; i < 16; i++) {\n      const angle = -Math.PI \/ 2 + i * Math.PI \/ 8;\n      const rr = i % 2 === 0 ? pickup.r : pickup.r * .80;\n      const px = Math.cos(angle) * rr;\n      const py = Math.sin(angle) * rr;\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    ctx.fillStyle = \"#4d2f20\";\n    ctx.beginPath();\n    ctx.ellipse(0, 1, 6, 9, 0, 0, Math.PI * 2);\n    ctx.fill();\n    ctx.strokeStyle = \"#fff8df\";\n    ctx.lineWidth = 2;\n    ctx.beginPath();\n    ctx.moveTo(-5, -2);\n    ctx.lineTo(5, -2);\n    ctx.moveTo(-5, 3);\n    ctx.lineTo(5, 3);\n    ctx.stroke();\n    ctx.restore();\n    drawStar(pickup.x - pickup.r, y - pickup.r * .82, pickup.r * .2, \"#fff1c9\", .95, pickup.spin);\n    return;\n  }\n\n  ctx.save();\n  const halo = ctx.createRadialGradient(pickup.x, y, 0, pickup.x, y, pickup.r * 2.3);\n  halo.addColorStop(0, \"rgba(255,248,223,.90)\");\n  halo.addColorStop(.52, pickup.type === \"honeyglassShield\" ? \"rgba(244,197,66,.44)\" : \"rgba(255,241,201,.46)\");\n  halo.addColorStop(1, \"rgba(255,241,201,0)\");\n  ctx.fillStyle = halo;\n  ctx.beginPath();\n  ctx.arc(pickup.x, y, pickup.r * 2.3, 0, Math.PI * 2);\n  ctx.fill();\n  ctx.translate(pickup.x, y);\n\n  if (pickup.type === \"honeyglassShield\") {\n    ctx.fillStyle = \"rgba(255,241,201,.78)\";\n    ctx.strokeStyle = \"#f4c542\";\n    ctx.lineWidth = 4;\n    ctx.beginPath();\n    ctx.arc(0, 0, 24, 0, Math.PI * 2);\n    ctx.fill();\n    ctx.stroke();\n    ctx.fillStyle = \"#b87333\";\n    ctx.beginPath();\n    ctx.moveTo(0, -14);\n    ctx.bezierCurveTo(13, -4, 13, 10, 0, 18);\n    ctx.bezierCurveTo(-13, 10, -13, -4, 0, -14);\n    ctx.fill();\n    drawStar(0, 1, 7, \"#fff8df\", .92, pickup.spin * .2);\n  } else {\n    ctx.save();\n    ctx.rotate(-.38);\n    ctx.fillStyle = \"#fff1c9\";\n    ctx.strokeStyle = \"#b87333\";\n    ctx.lineWidth = 3;\n    ctx.beginPath();\n    ctx.moveTo(-18, 19);\n    ctx.quadraticCurveTo(-24, -6, 8, -23);\n    ctx.quadraticCurveTo(25, -10, 17, 9);\n    ctx.quadraticCurveTo(5, 21, -18, 19);\n    ctx.fill();\n    ctx.stroke();\n    ctx.beginPath();\n    ctx.moveTo(-17, 18);\n    ctx.lineTo(14, -17);\n    ctx.stroke();\n    ctx.restore();\n    drawStar(7, -4, 8, \"#f4c542\", .95, pickup.spin * .2);\n  }\n\n  ctx.restore();\n}\n\n      function drawParticles() {\n        world.particles.forEach(function(p){\n          const alpha=clamp(p.life\/p.maxLife,0,1);\n          if(p.star)drawStar(p.x,p.y,p.size*alpha,p.color,alpha,p.life*7);\n          else{\n            ctx.globalAlpha=alpha;ctx.fillStyle=p.color;ctx.beginPath();ctx.arc(p.x,p.y,p.size*alpha,0,Math.PI*2);ctx.fill();ctx.globalAlpha=1;\n          }\n        });\n      }\n\n      function drawScene() {\n        const shakeX=state.shake>0?Math.sin(performance.now()*.05)*4*(state.shake\/.22):0;\n        const shakeY=state.shake>0?Math.cos(performance.now()*.04)*3*(state.shake\/.22):0;\n        ctx.save();\n        ctx.translate(shakeX,shakeY);\n        drawHoneyLibraryBackground();\n        drawArchiveDesk();\n        world.pickups.forEach(drawPickup);\n        world.obstacles.forEach(drawObstacle);\n        drawPlayer();\n        drawParticles();\n        ctx.restore();\n      }\n\n      function loop(timestamp) {\n        if(!state.lastTime)state.lastTime=timestamp;\n        const dt=Math.min(.028,(timestamp-state.lastTime)\/1000);\n        state.lastTime=timestamp;\n        if(state.mode===\"playing\")update(dt);\n        else{\n          updateParticles(dt);\n          state.shake=Math.max(0,state.shake-dt);\n        }\n        ctx.clearRect(0,0,DESIGN_W,DESIGN_H);\n        drawScene();\n        requestAnimationFrame(loop);\n      }\n\n      function toggleMute() {\n        state.muted=!state.muted;\n        soundButton.classList.toggle(\"qws-muted\",state.muted);\n        soundButton.setAttribute(\"aria-label\",state.muted?\"Unmute sound\":\"Mute sound\");\n        safeSet(MUTE_KEY,state.muted?\"1\":\"0\");\n        if(!state.muted)sound(\"click\");\n      }\n\n      function fullscreenElement() {\n        return document.fullscreenElement || document.webkitFullscreenElement || document.mozFullScreenElement;\n      }\n\n      function fullscreenActive() {\n        return fullscreenElement() === root || fallbackFullscreen;\n      }\n\n      function syncFullscreenMetrics() {\n        const active=fullscreenActive();\n        root.classList.toggle(\"qws-fullscreen-active\", active);\n        const viewport=window.visualViewport;\n        const width=Math.max(1,viewport ? (viewport.width ? viewport.width : window.innerWidth) : window.innerWidth);\n        const height=Math.max(1,viewport ? (viewport.height ? viewport.height : window.innerHeight) : window.innerHeight);\n        const offsetLeft=viewport ? (Number.isFinite(viewport.offsetLeft) ? viewport.offsetLeft : 0) : 0;\n        const offsetTop=viewport ? (Number.isFinite(viewport.offsetTop) ? viewport.offsetTop : 0) : 0;\n        const shellW=Math.min(width,height*16\/9);\n        const shellH=Math.min(height,width*9\/16);\n        root.style.setProperty(\"--qws-fs-shell-w\",shellW+\"px\");\n        root.style.setProperty(\"--qws-fs-shell-h\",shellH+\"px\");\n        root.style.setProperty(\"--qws-fs-center-x\",(offsetLeft+width\/2)+\"px\");\n        root.style.setProperty(\"--qws-fs-center-y\",(offsetTop+height\/2)+\"px\");\n      }\n\n      async function toggleFullscreen() {\n        sound(\"click\");\n        if (!fullscreenActive()) {\n          try {\n            const request = root.requestFullscreen || root.webkitRequestFullscreen || root.mozRequestFullScreen || root.msRequestFullscreen;\n            if (request) {\n              await Promise.resolve(request.call(root));\n            } else {\n              fallbackFullscreen = true;\n            }\n            try {\n              if (screen.orientation ? (screen.orientation.lock ? window.innerWidth < window.innerHeight : false) : false) {\n                await screen.orientation.lock(\"landscape\");\n              }\n            } catch (error) {}\n          } catch (error) {\n            fallbackFullscreen = true;\n            noteEl.textContent = \"Browser fullscreen was unavailable, so the Honey Library opened in fitted fullscreen mode.\";\n            noteEl.classList.add(\"qws-show\");\n            state.noteTimer = 2.4;\n          }\n        } else {\n          if (fullscreenElement() === root) {\n            try {\n              const exit = document.exitFullscreen || document.webkitExitFullscreen || document.mozCancelFullScreen || document.msExitFullscreen;\n              if (exit) await Promise.resolve(exit.call(document));\n            } catch (error) {}\n          }\n          fallbackFullscreen = false;\n          try { if (screen.orientation ? screen.orientation.unlock : false) screen.orientation.unlock(); } catch (error) {}\n        }\n        syncFullscreenMetrics();\n        requestAnimationFrame(resize);\n        setTimeout(function () { syncFullscreenMetrics(); resize(); }, 160);\n      }\n\n      function handleAction(action,event) {\n        if(event)event.preventDefault();\n        ensureAudio();\n        if(action===\"pause\"){\n          if(state.mode===\"playing\")pauseGame();\n          else if(state.mode===\"paused\")resumeGame();\n        }else if(action===\"sound\")toggleMute();\n        else if(action===\"fullscreen\")toggleFullscreen();\n        else if(action===\"main\"){\n          if(state.mode===\"paused\")resumeGame();else startGame();\n        }else if(action===\"restart\")startGame();\n        else if(action===\"jump\")jump();\n        else if(action===\"duck\")setDuck(true);\n      }\n\n      root.addEventListener(\"click\",function(event){\n        const button=event.target.closest(\"[data-qws-action]\");\n        if(!button)return;\n        const action=button.dataset.qwsAction;\n        if(action===\"jump\"||action===\"duck\")return;\n        handleAction(action,event);\n      });\n\n      root.querySelectorAll('[data-qws-action=\"jump\"]').forEach(function(button){\n        button.addEventListener(\"pointerdown\",function(event){handleAction(\"jump\",event);});\n        [\"pointerup\",\"pointercancel\",\"pointerleave\"].forEach(function(type){\n          button.addEventListener(type,function(event){event.preventDefault();input.jumpHeld=false;});\n        });\n      });\n\n      root.querySelectorAll('[data-qws-action=\"duck\"]').forEach(function(button){\n        button.addEventListener(\"pointerdown\",function(event){handleAction(\"duck\",event);});\n        [\"pointerup\",\"pointercancel\",\"pointerleave\"].forEach(function(type){\n          button.addEventListener(type,function(event){event.preventDefault();setDuck(false);});\n        });\n      });\n\n      canvas.addEventListener(\"pointerdown\",function(event){\n        event.preventDefault();\n        if(state.mode===\"playing\")jump();\n      });\n\n      \/\/ Activate game keyboard controls only after the player interacts with this game.\n      \/\/ This leaves WordPress page scrolling and other inputs unaffected.\n      root.tabIndex = 0;\n      root.addEventListener(\"pointerdown\", function () {\n        if (document.activeElement === root) return;\n        if (root.contains(document.activeElement)) return;\n        try { root.focus({ preventScroll: true }); } catch (error) { root.focus(); }\n      });\n\n      function gameHasKeyboardFocus() {\n        const active = document.activeElement;\n        if (!active) return false;\n        if (active !== root ? !root.contains(active) : false) return false;\n        const bounds = root.getBoundingClientRect();\n        if (bounds.bottom <= 0) return false;\n        return bounds.top < window.innerHeight;\n      }\n\n      document.addEventListener(\"keydown\",function(event){\n        if (!gameHasKeyboardFocus()) return;\n        const key=event.key;\n        if([\" \",\"ArrowUp\",\"ArrowDown\",\"w\",\"W\",\"s\",\"S\"].includes(key))event.preventDefault();\n        if(key===\" \"||key===\"ArrowUp\"||key===\"w\"||key===\"W\"){\n          if(!input.jumpHeld){input.jumpHeld=true;jump();}\n        }else if(key===\"ArrowDown\"||key===\"s\"||key===\"S\"){\n          if(!input.duckHeld)setDuck(true);\n        }else if(key===\"p\"||key===\"P\"){\n          if(state.mode===\"playing\")pauseGame();else if(state.mode===\"paused\")resumeGame();\n        }else if(key===\"m\"||key===\"M\")toggleMute();\n        else if(key===\"f\"||key===\"F\")toggleFullscreen();\n      });\n\n      document.addEventListener(\"keyup\",function(event){\n        if (!gameHasKeyboardFocus()) {\n          input.jumpHeld = false;\n          setDuck(false);\n          return;\n        }\n        const key=event.key;\n        if(key===\" \"||key===\"ArrowUp\"||key===\"w\"||key===\"W\")input.jumpHeld=false;\n        else if(key===\"ArrowDown\"||key===\"s\"||key===\"S\")setDuck(false);\n      });\n\n      \n      document.addEventListener(\"keydown\", function (event) {\n        if (event.key === \"Escape\" ? fallbackFullscreen : false) {\n          fallbackFullscreen = false;\n          syncFullscreenMetrics();\n          resize();\n        }\n      });\n\n      function fullscreenChanged() {\n        syncFullscreenMetrics();\n        scheduleResize(80);\n        scheduleResize(260);\n      }\n\n      \n      document.addEventListener(\"visibilitychange\", function () {\n        if (document.hidden ? state.mode === \"playing\" : false) pauseGame();\n      });\n\ndocument.addEventListener(\"fullscreenchange\",fullscreenChanged);\n      document.addEventListener(\"webkitfullscreenchange\",fullscreenChanged);\n      document.addEventListener(\"mozfullscreenchange\",fullscreenChanged);\n      window.addEventListener(\"resize\",function(){scheduleResize(60);});\n      window.addEventListener(\"orientationchange\",function(){scheduleResize(180);});\n      if(window.visualViewport)window.visualViewport.addEventListener(\"resize\",function(){scheduleResize(70);});\n      if(typeof ResizeObserver!==\"undefined\"){\n        const observer=new ResizeObserver(function(){scheduleResize(40);});\n        observer.observe(frame);\n      }\n\n      soundButton.classList.toggle(\"qws-muted\",state.muted);\n      soundButton.setAttribute(\"aria-label\",state.muted?\"Unmute sound\":\"Mute sound\");\n      world.player=createPlayer();\n      updateHud();\n      resize();\n      setMode(\"start\");\n      requestAnimationFrame(loop);\n      }\n\n      if (document.readyState === \"loading\") {\n        document.addEventListener(\"DOMContentLoaded\", initializeQuillwickShelfdash, { once: true });\n      } else {\n        initializeQuillwickShelfdash();\n      }\n    })();\n<\/script>\n<\/div>\n\n\n\n<h2 class=\"wp-block-heading\">Run Through the Honey Library<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Pip runs automatically from left to right while the archive route moves beneath him. The player is responsible for avoiding obstacles and choosing when to jump or duck.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Ground obstacles must be cleared with a jump. These include heavy Book Crates, groups of Catalogue Drawers, and Rolling Book Carts filled with lanterns, scrolls, and returned books. Each obstacle has a different width and height, so the timing required to pass it safely changes from one encounter to the next.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Upper obstacles require a different response. Hanging Quill Racks and Bookmark Ribbons stretch down from the ceiling of the route. Jumping into them is dangerous, so Pip must remain on the ground and duck until the obstacle has passed.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The game gradually increases its running speed. Early moments give players time to understand the rhythm of jumping and ducking, while later sections require quicker reactions and more precise control. Obstacles also appear with varied spacing, preventing the route from becoming a predictable sequence.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Jump, Duck, and Control Your Momentum<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Jumping is the most important movement in <strong>Quillwick Shelfdash<\/strong>. On desktop, players can press the Space key, W, or the Up Arrow. On mobile devices, the large Jump button provides a comfortable touch target. The game stage itself can also respond to a tap during active play.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A quick press creates a normal jump, while holding the jump input briefly can help Pip maintain upward momentum for a little longer. This allows players to adapt to obstacles of different sizes rather than relying on one fixed jump arc.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Ducking is controlled with S, the Down Arrow, or the mobile Duck button. Pip lowers his body and stretches forward, allowing him to pass beneath hanging objects. Ducking while airborne also causes Pip to fall faster, which can be useful when the next obstacle requires a quick return to the desk.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Successful runs depend on understanding when to commit to a jump and when to stay low. A poorly timed jump may clear one crate but leave Pip unable to duck beneath the next ribbon. Careful players learn to read the route ahead and preserve enough space for the following action.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Collect Golden Page Seals<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Golden Page Seals appear in small trails throughout the archive route. Some are placed close to the desk, while others form gentle arcs above obstacles. Collecting a seal increases the score and releases a burst of warm golden particles.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The safest route does not always collect every seal. Players may need to jump higher, remain airborne longer, or move closer to an obstacle to reach an entire trail. This creates a constant choice between survival and reward.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">In the game\u2019s story, each seal contains missing catalogue information. A longer run allows Pip to restore more shelf records, reading-room paths, and archive labels. Collecting seals therefore connects the scoring system directly to the larger mission of repairing the Honey Library.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Use the Honeyglass Shield<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The Honeyglass Shield is one of the rare power-ups found along the route. When collected, it surrounds Pip with a warm transparent barrier made from enchanted library glass.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The shield can absorb one collision. If Pip touches an obstacle while protected, the obstacle breaks away in a burst of parchment, amber light, and golden dust instead of ending the run. The shield is consumed immediately after protecting him.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Because it only blocks one impact, the Honeyglass Shield should not be treated as permanent invincibility. It is a second chance that allows the player to recover from one mistake. Skilled players may also use it strategically to attempt a difficult trail of Golden Page Seals that would otherwise be too risky.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A visible power badge shows when the shield is active and how much time remains before its enchantment fades.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Unlock the Quillwind Double Jump<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Quillwind is a second special power-up. It fills Pip\u2019s quill with a current of enchanted air, granting one additional jump while he is already airborne.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">After collecting Quillwind, the player can jump from the ground and then press Jump again before landing. The second jump lifts Pip upward, allowing him to correct a low first jump, reach high seal trails, or escape an obstacle combination that would normally be impossible.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The extra jump can only be used once during each airborne sequence. It becomes available again after Pip lands, as long as the Quillwind timer is still active.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">This power-up adds flexibility without removing the need for timing. Using the second jump too early may send Pip into a hanging rack, while saving it too long may result in a collision with a ground obstacle. The best moment depends on the shape of the route ahead.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Face Five Types of Library Obstacles<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Book Crates<\/strong> are sturdy wooden containers filled with returned volumes and bundles of parchment. Their uneven stacks make them dangerous ground obstacles that must be jumped.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Catalogue Drawers<\/strong> are heavy archive cabinets containing index cards and shelf records. They form wide barriers and often require players to begin jumping earlier than expected.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Rolling Book Carts<\/strong> move with the nightly delivery system. Their longer bodies make them harder to clear, especially when the game reaches higher speeds.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Hanging Quill Racks<\/strong> descend from the upper workshop rail. Pip must duck beneath their suspended handles and tools.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Bookmark Ribbons<\/strong> hang across the archive route in soft cloth panels. Although they appear gentle, their low position makes them dangerous to any courier who remains standing or jumps at the wrong moment.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The game avoids repeating the same obstacle too often and uses varied gaps between challenges. This keeps the route feeling organic and prevents players from relying entirely on memorized timing.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Increasing Speed and Endless Challenge<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The longer Pip survives, the faster the route becomes. Speed rises gradually rather than changing suddenly, allowing the challenge to grow naturally during each run.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">At low speed, players have time to study upcoming obstacles and prepare each move. At high speed, decisions must be made almost instantly. A wide crate followed by a hanging ribbon can become much more difficult when there is only a brief moment to land and duck.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">There is no final level or fixed finish line. The goal is to travel farther than before, collect more seals, and improve the best recorded distance. Each failed run becomes preparation for the next attempt.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Special milestone sounds celebrate every 1,000 points. These moments mark Pip\u2019s progress deeper into the Honey Library and provide small rewards during long runs.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Score and Best Distance<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The score increases continuously while Pip runs. Extra points are awarded for collecting Golden Page Seals, obtaining power-ups, and surviving a collision with the Honeyglass Shield.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The best score is stored locally in the browser. After a run ends, players can immediately restart and attempt to surpass their previous record.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Because the record is saved on the device, it remains available when returning to the game later. Clearing browser storage, using a private session, changing browsers, or opening the game on another device may reset the saved result.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">A Warm Honey Library World<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The visual world of <strong>Quillwick Shelfdash<\/strong> combines endless-runner action with the cozy atmosphere of the Honey Library. The route passes through walnut shelves, glowing windows, reading lamps, catalogue workstations, bookplates, quill tools, and softly illuminated archive decorations.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The background remains stable so players can clearly judge obstacle movement. The walnut desk beneath Pip scrolls horizontally, creating the sensation of fast travel without distracting vertical motion or unnecessary screen movement.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The color palette uses parchment cream, honey gold, amber, deep cocoa, walnut brown, and muted book green. Golden particles appear when seals are collected, shields break, milestones are reached, or Pip lands after a jump.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">All character artwork, obstacles, environmental decorations, and effects are rendered directly in the game. No external image library is required.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Sound, Pause, and Fullscreen Controls<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The game includes gentle WebAudio sound effects for jumping, ducking, collecting seals, activating power-ups, shield collisions, milestones, button presses, and game over. The sounds are light and responsive, supporting the action without overwhelming the Honey Library atmosphere.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The sound control can mute or restore all effects, and the preference is remembered locally by the browser.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The Pause button freezes the current run and opens a safe pause panel. Players can resume from the same moment or restart the route from the beginning.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Fullscreen mode expands the game to fit the available display. The fullscreen control remains accessible on the start, pause, and game-over panels, making it possible to change viewing mode without beginning a new run first.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Responsive Controls for Desktop and Mobile<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Quillwick Shelfdash<\/strong> is designed to work on desktop computers, laptops, tablets, and smartphones. Desktop players can use the keyboard or mouse, while mobile players receive large Jump and Duck controls placed near the lower corners of the stage.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The interface scales with the available screen while keeping important buttons readable and comfortable to touch. The canvas uses a capped pixel ratio to keep the artwork sharp without creating unnecessary performance demands on mobile devices.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Whether played during a short break or across a long record attempt, the game remains easy to start and challenging to master.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Carry the Golden Index Home<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Every run through the Honey Library is another attempt to restore the Great Golden Index. Every Golden Page Seal brings back a missing shelf number. Every obstacle crossed carries Pip deeper into the archive. Every new best score represents a longer and more successful courier route.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The controls may be simple, but the combination of increasing speed, varied obstacle spacing, jump timing, ducking, shield protection, and Quillwind creates a journey that rewards concentration and practice.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Open the archive route, tighten Pip\u2019s catalogue satchel, and follow the warm amber lamps through the shelves. In <strong>Quillwick Shelfdash<\/strong>, the Honey Library is counting on one very small courier to bring every lost page seal safely home.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Quillwick Shelfdash is a fast, cozy endless runner set inside the vast passageways of the Honey Library. Players guide Pip, a small archive courier bee, across a moving walnut desk while leaping over book crates, catalogue drawers, and rolling library carts. When the upper paths become crowded with hanging quill racks and long bookmark ribbons, &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-41","post","type-post","status-publish","format-standard","hentry","category-casual"],"_links":{"self":[{"href":"https:\/\/play.produsenpagarbrc.com\/index.php?rest_route=\/wp\/v2\/posts\/41","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=41"}],"version-history":[{"count":0,"href":"https:\/\/play.produsenpagarbrc.com\/index.php?rest_route=\/wp\/v2\/posts\/41\/revisions"}],"wp:attachment":[{"href":"https:\/\/play.produsenpagarbrc.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=41"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/play.produsenpagarbrc.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=41"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/play.produsenpagarbrc.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=41"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}